【问题标题】:Template argument deduction and SFINAE - using std::enable_if模板参数推导和 SFINAE - 使用 std::enable_if
【发布时间】:2015-01-19 21:03:47
【问题描述】:

我正在尝试过滤传递给重载函数模板的类型的方法。我正在使用 Visual Studio 2013。

三部分题:

  1. 为什么我的编译器不能推断出Blorg3
  2. TFoo2(argc) 产生编译器错误的原因与#1 相同吗?
  3. 有没有办法将模板参数传递给构造函数?

这里是示例代码:

#include <type_traits>

#define IFPTR(T,R) typename std::enable_if<std::is_pointer<T>::value, R>::type
#define IFINT(T,R) typename std::enable_if<std::is_integral<T>::value, R>::type

template <class T, IFINT(T, T)* = nullptr> int Blorg1(T n)  { return n + 1; }
template <class T, IFPTR(T, T)* = nullptr> int Blorg1(T n)  { return *n + 1; }
template <class T> IFINT(T, int) Blorg2(T n)                { return n + 1; }
template <class T> IFPTR(T, int) Blorg2(T n)                { return *n + 1; }
template <class T> int Blorg3(IFINT(T, T) n)                { return n + 1; }
template <class T> int Blorg3(IFPTR(T, T) n)                { return *n + 1; }

struct TFoo1 {
    template <class T, IFINT(T, T)* _ = nullptr> TFoo1(T n) { }
};
struct TFoo2 {
    template <class T> TFoo2(IFINT(T, T) n) { }
};

int main(int argc, char* argv[])
{
    Blorg1(argc); // intellisense not happy
    Blorg2(argc);
    Blorg3<int>(argc);  // why cant deduce?
    Blorg1(*argv); // intellisense not happy
    Blorg2(*argv); 
    Blorg3<char*>(*argv); // why cant deduce?
    (void)TFoo1(argc); // intellisense not happy
    (void)TFoo2(argc); // intellisense not happy and !!wont compile!!
    return 0;
}

【问题讨论】:

  • 1.因为模板参数只出现在 nested-name-specifier 中,这是一个非推导上下文(即导致模板参数不能从该特定参数的参数中推导) 2. 是 3.没有

标签: c++ templates c++11 sfinae template-argument-deduction


【解决方案1】:

回答 1/2 关于 SFINAE 不起作用的原因:

SFINAE模板参数推导 在这种情况下不能很好地配合使用。

或者,只要您知道事情发生的正确顺序,他们就会这样做。

必须保证推导有效,才能被视为在这种情况下调用的可能函数。

以下是一种技术含量较低的方法:

  1. 编译器会搜索与您尝试调用的函数匹配的可能函数签名。 [see overload resolution]

  2. 如果找到模板参数,它会查看它是否有效进行扣除。

这就是您遇到问题的原因。这两个事件发生的顺序就是为什么 SFINAE 对 Blorg1Blorg2TFoo1 起作用,而不是 Blorg3TFoo2

使用 Blorg3TFoo2 时,编译器无法插入您传递给模板类型的参数,因为它会创建无法解析的循环依赖。

template <class T> int Blorg3(IFINT(T, T) n)                { return n + 1; }
template <class T> int Blorg3(IFPTR(T, T) n)                { return *n + 1; }
Blorg3<char*>(*argv); // why cant deduce?

在这里解决Blorg3 中的SFINAE 需要知道T。但是,在 SFINAE 解决之前,T 是未知的。

TFoo2 不起作用的原因也是如此。

第 3 部分 - 关于模板和构造函数

是的,您可以将模板参数传递给构造函数,但前提是您必须通过推断来完成,例如 TFoo1 所做的。

您不能显式将模板参数传递给构造函数。

【讨论】:

  • 感谢您的解释!由于重载解析忽略了返回类型,所以两个 Blorg1 都将匹配签名检查。模板参数很容易推断为 blorg1 和 2 个 blorg1 是在给定类型的情况下生成的。其中一个函数未能通过后续的语法检查。 blorg2 的工作方式相同。当达到不可推演的版本 blorg3 时,它不会生成函数。
【解决方案2】:

为什么我的编译器不能推导出 Blorg3?

std::enable_if&lt;std::is_pointer&lt;T&gt;::value, R&gt;::type 中,::type 指的是依赖于模板参数TR 的嵌套名称。这是一个非推导上下文(§14.8.2.5/5),因此编译器不会推导模板参数。

这就是 TFoo2(argc) 生成编译器错误的原因吗?

是的,构造函数模板必须能够推断出它的模板参数,而在这种情况下它不能。

是否有向构造函数提供模板参数的语法?

不,正如我已经提到的,你不能明确地这样做,它们必须被推导,或者模板参数必须具有默认参数。

【讨论】:

  • @MichaelGazonda 我不明白你的意思
  • "是否有向构造函数提供模板参数的语法?" - 是的,扣除会做到这一点。没有指定是否需要明确传递...只是需要完成。
  • @MichaelGazonda 好的,同意,但考虑到前面问题的上下文,我似乎很清楚 OP 是在询问明确指定构造函数的模板参数。
  • 表达我的意思的正确方式是什么?我认为“推断”是编译器在您不明确使用 . 时必须做的事情
  • @johnnycrash 这确实是扣除的意思,在这种情况下是禁止的。我认为你的措辞很清楚,但如果你想改变它,你可以说向构造函数显式提供模板参数
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-09-18
  • 1970-01-01
  • 2012-06-18
  • 1970-01-01
  • 2017-12-11
相关资源
最近更新 更多