C++ 未定义模板的隐式实例化';类别';

C++ 未定义模板的隐式实例化';类别';,c++,function,templates,constants,overloading,C++,Function,Templates,Constants,Overloading,在我的库中尝试为常量和非常量模板参数提供函数时,我遇到了一个奇怪的问题。以下源代码是一个最小的示例现象: #include <iostream> template<typename some_type> struct some_meta_class; template<> struct some_meta_class<int> { typedef void type; }; template<typename some_t

在我的库中尝试为常量和非常量模板参数提供函数时,我遇到了一个奇怪的问题。以下源代码是一个最小的示例现象:

#include <iostream>


template<typename some_type>
struct some_meta_class;

template<>
struct some_meta_class<int>
{
    typedef void type;
};



template<typename some_type>
struct return_type
{
    typedef typename some_meta_class< some_type >::type test;

    typedef void type;
};



template<typename type>
typename return_type<type>::type foo( type & in )
{
    std::cout << "non-const" << std::endl;
}

template<typename type>
void foo( type const & in )
{
    std::cout << "const" << std::endl;
}


int main()
{
    int i;

    int const & ciref = i;
    foo(ciref);
}
#包括
模板
构造一些元类;
模板
构造一些元类
{
typedef-void型;
};
模板
结构返回类型
{
typedef typename some_meta_class::类型测试;
typedef-void型;
};
模板
typename return\u type::type foo(type&in)
{

原因是函数调用解析的执行方式,以及模板参数的推导和替换

  • 首先,执行名称查找。这将为您提供两个具有匹配名称的函数
    foo()

  • 其次,执行类型推断:对于具有匹配名称的每个模板函数,编译器尝试推断出函数模板参数,这将产生一个可行的匹配。您得到的错误发生在这个阶段

  • 第三,重载解析进入游戏。这只有在执行了类型推断并确定了解析调用的可行函数的签名之后,这是有意义的:编译器只有在找到所有函数的确切签名后才能有意义地解析您的函数调用迪迪斯

  • 您得到与非常量重载相关的错误的事实并不是因为编译器选择它作为解析调用的最可行候选(这将是步骤3),而是因为编译器在步骤2中实例化其返回类型以确定其签名时产生错误

    是<强>不完全明显<>强>为什么这会导致一个错误,因为人们可能会期望<强> sFIEA<强>应用(替换失败不是一个错误)。

    template<typename T> struct X { };
    
    template<typename T> typename X<T>::type f(T&) { }  // 1
    template<typename T> void f(T const&) { }           // 2
    
    int main()
    {
        int const i = 0;
        f(i); // Selects overload 2
    }
    
    改变的是,重载1不再返回
    X::type
    ,而是返回
    R::type
    。这反过来又与
    X::type
    相同,因为
    R
    中的
    typedef
    声明,所以人们可能期望它产生相同的结果。但是,在这种情况下,您会得到编译错误。为什么?

    本标准给出了答案(第14.8.3/8段):

    如果替换导致无效的类型或表达式,则类型推断将失败。无效的类型或表达式是使用替换的参数编写时格式错误的类型或表达式。[…]只有函数类型及其模板参数类型的即时上下文中的无效类型和表达式才能导致推断失败

    显然,第二个示例(以及您的示例)在嵌套上下文中生成错误,因此SFINAE不适用。我相信这回答了您的问题

    顺便说一句,值得注意的是,自C++03以来,发生了变化,更一般地说(第14.8.2/2段):

    […]如果模板参数或函数模板的函数类型中的替换导致类型无效,则类型推断失败。[…]


    如果您对事情发生变化的原因感到好奇,可能会给您一个想法。

    它目前正处于生成可能的有效重载列表的阶段,并且由于它无法正确实例化return\u type,因此它退出了那里(注释test in return\u type的typedef以了解我的意思)很好的分析,结合了侦探工作和实用语言律师。
    template<typename T> struct X { };
    
    template<typename Y>
    struct R { typedef typename X<Y>::type type; };
    
    // Notice the small change from X<T> into R<T>!
    template<typename T> typename R<T>::type f(T&) { }  // 1
    template<typename T> void f(T const&) { }           // 2
    
    int main()
    {
        int const i = 0;
        f(i); // ERROR! Cannot instantiate R<int const>
    }