C++ 模板化函数可以';t转换为';int';取消注册

C++ 模板化函数可以';t转换为';int';取消注册,c++,c++11,templates,template-meta-programming,C++,C++11,Templates,Template Meta Programming,TL;医生: 为什么模板化函数不能访问与非模板化函数相同的转换 struct A { A(std::nullptr_t) {} }; template <typename T> A makeA(T&& arg) { return A(std::forward<T>(arg)); } void foo() { A a1(nullptr); //This works, of course A a2(0); //This wo

TL;医生:

为什么模板化函数不能访问与非模板化函数相同的转换

struct A {
    A(std::nullptr_t) {}
};

template <typename T>
A makeA(T&& arg) {
    return A(std::forward<T>(arg));
}

void foo() {
    A a1(nullptr); //This works, of course
    A a2(0); //This works
    A a3 = makeA(0); //This does not
}

为什么编译器决定将零视为int

因为它是一个整数

文本0是一个文本。文字可以做有趣的事情。字符串文字可以转换为
const char*
const char[N]
,其中
N
是字符串+NUL终止符的长度。文字0也可以做有趣的事情;它可以用来初始化带有空指针常量的指针。它可以用来初始化类型为
nullptr\u t
的对象。当然,它可以用来创建一个整数

但一旦它作为一个参数被传递,它就不再是一个神奇的编译器构造了。它变成了一个具有具体类型的实际C++对象。当涉及到模板参数推断时,它得到了最明显的类型:
int

一旦它变为
int
,它就不再是文本0,其行为与任何其他
int
完全相同。除非在
constepr
上下文中使用它(如
int(0)
),在该上下文中编译器可以发现它确实是一个文本0,因此可以发挥其神奇的特性。函数参数从来都不是constexpr,因此它们不能参与其中。

参见[conv.ptr]/1:

空指针常量是值为零的整数文本或类型为
std::nullptr\u t
的prvalue。空指针常量可以转换为指针类型;结果是该类型的空指针值[…]

因此整数文本
0
可以转换为空指针。但是,如果您试图将其他一些整数值(不是文字)转换为指针类型,则上面的引号不适用。事实上,没有其他从整数到指针的隐式转换(因为[conv.ptr]中没有列出这种转换),所以代码失败


注:显式转换包含在[expr.reinterpret.cast]/5中。

0是一个空指针,(int)(0)不是为什么编译器决定将零视为int?整数文本是int,如果它符合“我们有一个可以从std::nullptr_t(这里称为a)构造的类”,因此,在代码库中有很多地方有人给实例赋值为零。”我质疑一个对象的设计,它可以从
nullptr
初始化,但不能从非空指针初始化仅在VC++(和intellisense)中工作,因此这看起来像是答案。“函数参数从来都不是
constexpr
,因此它们不能参与其中。”啊,是的。非常感谢。你给了我希望,然后把它砸在了石头上。@LyndenShields故事的寓意是:避免在需要空指针的地方使用
0
,现在当我的同事抱怨时,我可以指出这个问题:D
struct A {
    A(){}
    A(std::nullptr_t) {}
};

template <typename T>
struct Wrapper {
    A a;
    Wrapper(const A& a):a (a) {}

    template <typename T>
    Wrapper(T&& t): a(std::forward<T>(t)){}

    Wrapper(){}
};

void foo2() {
    A a1;
    a1 = 0; // This works

    Wrapper<A> a2;
    a2 = 0; //This does not
}