Implicit initialization by argument deduction for a templated class array

67 views Asked by At

I have an item class, thats really a wrapper around an int. I have a templated node class. I pass item class to it as template parameter.

I want to create an array of the templated node class, but compiler does not like it. When I create a single object using the same syntax, it works. Why cant it deduce the array elements to initialize array of templated class? What is the correct way of creating an array of objects of a templated class?

Heres item class, which is a wrapper around an int.

using namespace std;

class bItem {
private :     
    int item{INT_MIN};
public:
    bItem() : item(INT_MIN) {}
    bItem(int val) : item(val) {}
    int getItem() { return item; }
    bItem operator==(const bItem& val) {
        return item == val.item;
    }
    bItem operator>(const bItem& val) {
        return item > val.item;
    }
    bItem operator<(const bItem& val) {
        return item < val.item;
    }
    friend ostream& operator<<(ostream& os, const bItem& item) {
        os << "value:" << item.item << endl;
        return os;
    }
};

Heres the templated Node class

#include <iostream>
using namespace std;
template<class itemType>
class bNode {
    itemType item{};
    bNode* prev{};
    bNode* next{};
public:
    bNode() : item(), prev(nullptr), next(nullptr) {}
    bNode(itemType _item) : item(_item), prev(nullptr), next(nullptr) {}
    ~bNode() {
        delete prev;
        delete next;
    }
    
    friend ostream& operator<<(ostream& os, const bNode& node) {
        os << "node " << node.item<< endl;
        return os;
    }
};

and heres the main()

int main()
{    
    bNode<bItem> nodes[5]{ 1,2,3,4,5 }; //this does not compile
    bNode<bItem> node2(6); //this compiles ok

}

and here is the error message

error C2440: 'initializing': cannot convert from 'int' to 'bNode<bItem>'
message : No constructor could take the source type, or constructor overload resolution was ambiguous
1

There are 1 answers

4
leslie.yao On BEST ANSWER

In aggregate initialization of array, the elements are copy-initialized from the corresponding clause of the initializer list. That means the element with type bNode<bItem> needs to be copy-initialized from the int, which requires two user-defined implicit conversions, from int to bItem and from bItem to bNode, which is not allowed.

Each direct public base, (since C++17) array element, or non-static class member, in order of array subscript/appearance in the class definition, is copy-initialized from the corresponding clause of the initializer list.

As the workaround you could impose restrictions on the times of implicit conversions, e.g.

bNode<bItem> nodes[5] = { bItem(1),bItem(2),bItem(3),bItem(4),bItem(5) };

Or you can add another constructor taking int to bNode, then it could be copy-initialized from int too.

// forward it to bNode::bNode(itemType)
bNode(int val) : bNode(itemType(val)) {}

BTW: bNode<bItem> node2(6); performs direct initialization, which requiers only one implicit conversion (from int to bItem), and the converted bItme is passed to the constructor of bNode to construct node2. It doesn't have such issue as copy initialization. (Copy-initialization like bNode<bItem> node2 = 6; doesn't work either.)

In addition, the implicit conversion in copy-initialization must produce T directly from the initializer, while, e.g. direct-initialization expects an implicit conversion from the initializer to an argument of T's constructor.