C++模板参数推导失败

C++模板参数推导失败,c++,templates,sfinae,typetraits,C++,Templates,Sfinae,Typetraits,为什么编译器可以使用此代码推断T: #include <vector> template<typename T> void foo(T& t) {} int main(void) { std::vector<uint8_t> vec = { 1,2,3 }; foo(vec); return 0; } #include <vector> #include <type_traits> templat

为什么编译器可以使用此代码推断T:

#include <vector>

template<typename T>
void foo(T& t) {}

int main(void) {
    std::vector<uint8_t> vec = { 1,2,3 };
    foo(vec);
    return 0;
}
#include <vector>
#include <type_traits>

template<typename T>
void foo(typename std::enable_if<true, T>::type& t) {}

int main(void) {
    std::vector<uint8_t> vec = { 1,2,3 };
    foo(vec);
    return 0;
}
但此代码失败:

#include <vector>

template<typename T>
void foo(T& t) {}

int main(void) {
    std::vector<uint8_t> vec = { 1,2,3 };
    foo(vec);
    return 0;
}
#include <vector>
#include <type_traits>

template<typename T>
void foo(typename std::enable_if<true, T>::type& t) {}

int main(void) {
    std::vector<uint8_t> vec = { 1,2,3 };
    foo(vec);
    return 0;
}

我想使用第二个构造,根据传递的类方法的存在性,在两个模板函数之间进行选择。

在第二种情况下,您有一个,换句话说,编译器无法推断类型

非推断上下文的最简单示例是

template<typename T>
struct Id
{
    using type = T;
};

template<typename T>
void foo(typename Id<T>::type arg); // T cannot be deduced

正如vsoftco所解释的,您有一个非推断上下文

对于SFINAE,您可以使用以下选项之一:

template<typename T>
std::enable_if_t<condition_dependent_of_T, ReturnType>
foo(T& t) {}


为了将问题可视化,让我们分析一个示例:

template <class>
struct foo { 
    using type = float;
};

template <>
struct foo<bool> {
    using type = int;
};

template <>
struct foo<int> {
    using type = int;
};

template <class T>
void bar(foo<T>::type t) { }

int main() {
   bar(int{}); 
}

现在在barint{}线上;这两种类型bool和int都与模板参数T匹配。那么应该推导哪一个值?这只是一个例子,为什么非推断上下文是严格必要的

为了解决这个问题,需要分离上下文进行演绎,并启用_if。通常的做法是在函数返回值中使用enable_if,如下所示:模板typename std::enable_if::type fooT&t{}可能有一个专门化std::enable_if,它包含一个using type=std::vector。编译器如何知道何时它只有参数类型?它工作得很好。但我不明白为什么T的条件依赖于T必须真的依赖于T。当我把计算常量放在那里时,代码无法编译,这是为什么?@omicrons T是从参数T&T推导出来的,而不是从enable\u if推导出来的。非推断上下文是不可推断的,因为编译器从中推断类型非常困难,有时甚至不可能。如果启用取决于T,因为T将被推导,那么SFINAE将开始。@Jarod42我希望可以合并答案,所以:我现在明白了。当我放置常量时,如果::type对两个调用的计算结果相同,则启用\ u。当我根据T参数化它时,它会根据不同的调用得到不同的评估。谢谢好啊但是编译器通过bar调用知道这些不明确的专门化。当foo不存在或foo时,就没有歧义了。@omicrons是的,但是想象一下,在一组已经非常复杂的规则中,需要添加多少规则和异常。有时编译器可能不知道:如果您的专门化是在不同的编译单元中定义的呢?编译器没有对其他单元进行任何假设。@vsoftco所以提供此限制是为了不使编译器过于复杂?@omicrons我认为这确实是不可能的,请参阅编辑后的注释。