【问题标题】:C++ template parameter deduction failsC++模板参数推导失败
【发布时间】:2016-09-20 17:57:51
【问题描述】:

为什么编译器可以用这段代码推导出 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;
}

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

【问题讨论】:

  • 可能有一个专业化std::enable_if&lt;true, MyType&gt;,其中包含一个using type = std::vector&lt;uint8_t&gt;。编译器如何知道它何时只有参数类型?

标签: c++ templates sfinae typetraits


【解决方案1】:

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

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

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

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

【讨论】:

  • 为了解决这个问题,需要将context for deduction和enable_if分开。常见的做法是在函数返回值中使用 enable_if,像这样:template&lt;typename T&gt; typename std::enable_if&lt;std::is_same&lt;T, std::vector&lt;uint8_t&gt;&gt;::value, void&gt;::type foo(T&amp; t) {}
【解决方案2】:

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

对于 SFINAE,您可以使用以下方法之一:

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

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

【讨论】:

  • 效果很好。但我不明白为什么 condition_dependent_of_T 必须真正依赖于 T。当我把评估常量放在那里时,代码无法编译,这是为什么呢?
  • @omicronns T 是从参数 T&amp; t 推导出来的,而不是从 enable_if 推导出来的。非推导上下文是不可推导的,因为编译器很难(有时甚至不可能)从中推导类型。 enable_if 应该依赖于 T,因为 T 将被推导出来,然后 SFINAE 将启动。
  • @Jarod42 我希望可以在 SO 上合并答案 :)
  • 我现在明白了。当我放置常量 enable_if::type 时,两个调用的评估结果相同。当我由于 T 对其进行参数化时,它会针对不同的调用进行不同的评估。谢谢!
【解决方案3】:

为了形象化问题,让我们分析一个例子:

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

现在在bar(int{}); 行中,boolint 都与模板参数T 匹配。那么应该推导出哪一个值呢?这只是为什么非推导上下文是绝对必要的一个例子!

【讨论】:

  • 好的。但是编译器通过 bar 调用知道那些模棱两可的专业化。当 foo 不存在(或 foo)时,就没有歧义了。
  • @omicronns 是的,但是想象一下需要将多少规则和例外添加到已经非常复杂的规则集中。有时编译器可能不知道:如果您的专业化定义在不同的编译单元中怎么办?编译器不对其他单元做任何假设。
  • @vsoftco 所以这个限制是为了不让编译器过于复杂?
  • @omicronns 我认为这真的不可能,请参阅编辑后的评论。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-04-28
  • 2014-09-22
  • 2011-07-28
  • 1970-01-01
  • 2012-02-07
  • 2022-01-06
  • 1970-01-01
相关资源
最近更新 更多