15

Template constructor in a class template - how to explicitly specify template argument for the 2nd parameter?

compile error when tried to explicit specify template argument for constructor 2. How should I do it if I really want to explicit call constructor 2 ?

Please note this is the same situation for boost::shared_ptr when you want to explicitly specify the deleter type.

N.B. For non-construction function foo(), explicitly specify works fine.

N.B I know it works fine without specify the 2nd one explicitly for the constructor 2 as template argument deduction normally just works fine, I am just curious how to specify it explicitly.

template<class T> class TestTemplate {
public:
    //constructor 1
    template<class Y> TestTemplate(T * p) {
        cout << "c1" << endl;
    }

    //constructor 2
    template<class Y, class D> TestTemplate(Y * p, D d) {
        cout << "c2" << endl;
    }

    template<class T, class B>
    void foo(T a, B b) {
        cout << "foo" << endl;
    }
};

int main() {
    TestTemplate<int> tp(new int());//this one works ok call constructor 1
    //explicit template argument works ok
    tp.foo<int*, string>(new int(), "hello");

    TestTemplate<int> tp2(new int(),2);//this one works ok call constructor 2

    //compile error when tried to explicit specify template argument for constructor 2
    //How should I do it if I really want to explicit call constructor 2?
    //TestTemplate<int*, int> tp3(new int(), 2); //wrong
    //TestTemplate<int*> tp3<int*,int>(new int(), 2); //wrong again

    return 0;
}
RoundPi
  • 5,819
  • 7
  • 49
  • 75

3 Answers3

29

Fixing your code, the following would work:

template<class T> class TestTemplate {
public:
    //constructor 1
    template<class Y> TestTemplate(Y * p) {
        cout << "c1" << endl;
    }

    //constructor 2
    template<class Y, class D> TestTemplate(Y * p, D d) {
        cout << "c2" << endl;
    }

    template<class A, class B>
    void foo(A a, B b) {
        cout << "foo" << endl;
    }
};

int main() {
    TestTemplate<int> tp(new int());

    tp.foo<int*, string>(new int(), "hello");

    TestTemplate<int> tp2(new int(),2);
}

You cannot use T for the class template parameter and the constructor template parameter. But, to answer your question, from [14.5.2p5]:

Because the explicit template argument list follows the function template name, and because conversion member function templates and constructor member function templates are called without using a function name, there is no way to provide an explicit template argument list for these function templates.

Therefore, you cannot explicitly specify template arguments for constructor.

Jesse Good
  • 50,901
  • 14
  • 124
  • 166
  • What's `14.5.2p5`? – John May 14 '22 at 12:59
  • It's telling you where the quote came from in the ISO C++ standard. It means "section 14.5.2 part 5". However, with the latest draft, is is now in [section 13.10.2 part 8](https://eel.is/c++draft/temp.fct.spec#temp.arg.explicit-8). – Jesse Good May 14 '22 at 22:05
11

You can't explicitly specify the template arguments for a constructor, because the constructor has no name on its own, and so there's no syntax for it.

But, you can ensure that correct template arguments are inferred, by

  • casting actual arguments, and/or

  • introducing "artificial" extra arguments just to carry type information, if necessary, and/or

  • use a factory function.

For example, you can define

template< class Type > struct TypeCarrier{ typedef Type T; };

struct MyClass
{
    template< class Type >
    MyClass( TypeCarrier< Type > ) { ... }
};

...
MyClass o( TypeCarrier<int>() );

But don't get carried away with such techniques.

Instead, if the apparent need to explicitly specify constructor template arguments pops up, think about whether the design is really sound?

Perhaps you can use some simpler design if you reflect on what it’s for?

Cheers and hth. - Alf
  • 142,714
  • 15
  • 209
  • 331
4

You can explicitly specify the template arguments for your calls to foo because those member functions foo have names -- and the template arguments are part of that name.

That doesn't work with constructors because a constructor has no name. You can't (directly) call a constructor. A constructor is of course called when you create an object, but the call is generated code.

David Hammen
  • 32,454
  • 9
  • 60
  • 108
  • -1 This answer is correct up to the "can't call constructor" nonsense. It's an old beginners' meme, with discussion going back decades. Very hard to eradicate. But start with looking up the definition of default constructor in the standard. – Cheers and hth. - Alf Oct 21 '12 at 22:01
  • Yea I know I can do that with foo see my N.B above. But I wanna do it for my constructor 2 &a constructor has name too it's the name of class... Are you saying it cannot be done regarding my question ? – RoundPi Oct 21 '12 at 22:03
  • @Gob00st: you can't, cause it has no name. listen to the ["no name" song](http://www.youtube.com/watch?v=1EQpYUNP1Co) by Richard Wakeman on his "1984" album. – Cheers and hth. - Alf Oct 21 '12 at 22:04
  • Note that I said "directly", Alf: You cannot call a constructor *directly*. How can you? Constructors don't have names. – David Hammen Oct 21 '12 at 22:25
  • Just don't mention that "can't call" at all. Adding evasive language that allows for an unnatural interpretation where it's not literally false, does not make it better. It makes it *worse*. – Cheers and hth. - Alf Oct 21 '12 at 22:27
  • Is there a section in standard talks about this constructor cannot be invoked directly ? I know we can do placement new which only invoke constructor on a pre-allocated memory. – RoundPi Oct 21 '12 at 22:35
  • 1
    @Gob00st - 12.1, paragraph 1 starts with "Constructors do not have names." paragraph 2 continues this with "Because constructors do not have names, **they are never found during name lookup**; however an explicit type conversion using the functional notation will cause a constructor to be called to initialize an object." – David Hammen Oct 21 '12 at 22:55
  • Thanks David, just found that as well. – RoundPi Oct 21 '12 at 23:11
  • @David: the reason that you *failed to find* any statement about constructors being non-callable, and instead had to add misleading emphasis to a statement about name lookup, is because they aren't uncallable (as you have pointed out that you know, hence using the weasel word "direct", and as one is required to understand in order to understand e.g. the standard's definition of a default constructor). – Cheers and hth. - Alf Oct 22 '12 at 00:33
  • @Gob00st: you shouldn't thank him for pulling one over your eyes. the statement he emphasized in boldface is about name lookup. there is no statement in the standard about constructors being non-callable. on the contrary, the standard talks in a great many places about calling constructors. and some things are defined in terms of that notion. – Cheers and hth. - Alf Oct 22 '12 at 00:36
  • @Cheersandhth.-Alf - Stop that. There are plenty of people here at this site, many with more points than you, who use that exact phrase I did. You can't directly call a constructor. – David Hammen Oct 22 '12 at 01:31
  • @David: the reason that you **failed to find** any statement about constructors being non-callable, and instead had to add misleading emphasis to a statement about name lookup, is because they aren't uncallable (as you have pointed out that you know, hence using the weasel word "direct", and as one is required to understand in order to understand e.g. the standard's definition of a default constructor). – Cheers and hth. - Alf Oct 22 '12 at 01:35