类模板中的模板构造函数 - 如何明确指定第二个参数的模板参数?

15

类模板中的模板构造函数 - 如何显式指定第二个参数的模板参数?

当尝试显式指定构造函数2的模板参数时,会出现编译错误。如果我真的想要显式调用构造函数2,该怎么办?

请注意,在想要显式指定删除器类型时,boost::shared_ptr也会出现同样的情况。

注意:对于非构造函数foo(),显式指定可以正常工作。

注意:我知道在构造函数2中不显式指定第二个参数的模板参数通常也能正常工作,因为模板参数推导通常可以正常工作,我只是好奇如何显式指定它。

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;
}

使用tempate<class T> TestTemplate(T * p)会导致错误(gcc 4.6.3):"error: shadows template parm 'class T'"。使用void foo()同样会出现相同的问题。当我将T替换为例如X时,就可以解决这个问题。 - Olaf Dietsche
你使用的是哪个编译器?我甚至无法编译你的代码。 - gogoprog
@gogoprog 你不是唯一一个。 - WhozCraig
gcc 说明了一切,VC10 又被证明是愚蠢的! - RoundPi
3个回答

29
修复您的代码,以下内容可以正常工作:

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);
}

您不能同时使用 T 作为类模板参数和构造函数模板参数。但是,根据 [14.5.2p5] 的规定:

由于显式模板参数列表跟随函数模板名称,并且由于转换成员函数模板和构造函数成员函数模板在调用时不使用函数名称,因此无法为这些函数模板提供显式模板参数列表。

因此,您无法显式指定构造函数的模板参数。

14.5.2p5 是什么? - John
它告诉你引用来自ISO C++标准的哪个部分。这意味着“第14.5.2节第5部分”。然而,根据最新的草案,现在在第13.10.2节第8部分中。 - Jesse Good

11

你不能显式地为构造函数指定模板参数,因为构造函数本身没有名称,所以没有针对它的语法。

但是,你可以通过以下方式确保正确的模板参数被推导

  • 转换实际参数,和/或

  • 引入“人工”额外参数来携带类型信息(如果需要),和/或

  • 使用一个工厂函数。

例如,你可以定义:

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

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

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

但是不要过度使用这样的技巧。

相反,如果明显需要显式指定构造函数模板参数,请考虑设计是否真正合理?

如果你反思一下它的用途,也许可以使用一些更简单的设计。


感谢您提供的好的元技巧。不,这不是设计问题,只是想尝试一些东西。 - RoundPi

4

您可以明确指定调用 foo 的模板参数,因为这些成员函数 foo 有名称 -- 并且模板参数是该名称的一部分。

但对于构造函数来说,这种方式行不通,因为构造函数没有名称。您不能(直接)调用构造函数。当然,在创建对象时会调用构造函数,但这个调用是生成的代码。


-1 这个答案在“无法调用构造函数”的荒谬问题上是正确的。这是一个老的初学者梗,讨论已经持续了几十年。很难根除。但是首先要查找标准中默认构造函数的定义。 - Cheers and hth. - Alf
是的,我知道我可以用foo做到这一点,请参见我的上面的N.B。但我想为我的构造函数2做到这一点,而且构造函数也有一个名称,它是类的名称...你是说无法回答我的问题吗? - RoundPi
@Gob00st:你做不到,因为它没有名字。听听理查德·瓦基曼在他的《1984》专辑中演唱的“无名之歌”。 - Cheers and hth. - Alf
不要提到“不能调用”这个问题。添加含糊语言,使其可以被非自然的解释所理解,并不会让情况变得更好,反而会让它变得更糟。 - Cheers and hth. - Alf
1
@Gob00st - 12.1,第1段开头是“构造函数没有名称。”第2段继续说明:“因为构造函数没有名称,所以它们在名称查找期间永远不会被找到;但是使用函数符号的显式类型转换将导致调用构造函数来初始化对象。” - David Hammen
显示剩余7条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接