【问题标题】:C++ Nesting SFINAE Template Produces Compilation ErrorC++ 嵌套 SFINAE 模板产生编译错误
【发布时间】:2021-11-10 05:13:08
【问题描述】:

我正在尝试为自定义模板类创建一个加法运算符,其中允许第一个参数是我的类的实例或基本数字类型。我的操作符的定义类似于下面的示例代码:

#include <type_traits>

template<typename T>
struct MyTemplateStruct {
    T val;
};

template<typename T, typename U>
struct MyCommonType {
    typedef std::common_type_t<T, U> type;
};
template<typename T, typename U>
using MyCommonTypeT = typename MyCommonType<T, U>::type;

template<typename T, typename U>
MyTemplateStruct<MyCommonTypeT<T, U>> operator +(
    MyTemplateStruct<T> const& a, MyTemplateStruct<U> const& b)
{
    return { a.val + b.val };
}

template<typename T, typename U>
MyTemplateStruct<MyCommonTypeT<T, U>> operator +(
    T const a, MyTemplateStruct<U> const& b)
{
    return { a + b.val };
}

int main()
{
    MyTemplateStruct<double> a{ 0 }, b{ 0 };
    a = a + b;

    return 0;
}

我的预期是,由于 SFINAE,尝试使用 T = MyTemplateStruct&lt;double&gt;, U = double 实例化第二个运算符定义导致的编译错误只会将该模板从潜在匹配列表中排除。但是,当我编译时,我收到以下错误:

/usr/include/c++/7/type_traits: 使用 common_type_t = typename std::common_type::type [with _Tp = {MyTemplateStruct, double}]'替换'template: main.cpp:18:38: 需要来自‘struct MyCommonType
main.cpp:31:39: 替换‘template MyTemplateStruct::type> operator+(T, const MyTemplateStruct&) [with T = MyTemplateStruct; U = 双]'
main.cpp:40:13:从这里需要 /usr/include/c++/7/type_traits:2484:61: error: no type named ‘type’ in ‘struct std::common_type, double>’

如果我在运算符定义中直接使用std::common_type_t,而不是使用我的包装器模板MyCommonTypeT,那么SFINAE 会按我的预期工作并且没有错误。当我必须将对 std::common_type 的调用包装在另一个模板中时,如何编译上述代码?

【问题讨论】:

  • 查看this answer,看来我可能需要添加一个取值true_type/false_type 的模板,具体取决于我的模板是否接受提供的模板参数。然后,声明我的结构采用默认参数指示是否支持类型 T/U,并且仅在支持时提供typedef。看起来有点笨拙,但它可能会起作用。

标签: c++ templates sfinae


【解决方案1】:

您需要使MyCommonTypeT&lt;T, U&gt;(即MyCommonType&lt;T, U&gt;::type)本身无效,在MyCommonType 中声明type 时,std::common_type_t&lt;T, U&gt; 无效是不够的。例如你可以特化MyCommonType,当MyTemplateStruct被指定为模板参数时,type没有被声明。

template<typename T, typename U>
struct MyCommonType {
    typedef std::common_type_t<T, U> type;
};
template<typename T, typename U>
struct MyCommonType<MyTemplateStruct<T>, U> {};
template<typename T, typename U>
struct MyCommonType<T, MyTemplateStruct<U>> {};

LIVE

【讨论】:

  • 通过这样做,我必须明确禁用我想使用的每种类型的模板。有没有办法只在 TU 类型通过 std::is_arithmetic_v&lt;T&gt; &amp;&amp; std::is_arithmetic_v&lt;U&gt; 时启用它?我见过人们只添加默认模板参数的代码,但不知道这是否是最好的方法。例如,template&lt;typename T, typename U, bool=std::is_arithmetic_v&lt;T&gt; &amp;&amp; std::is_arithmetic_v&lt;U&gt;&gt; 为空,然后在第三个参数为 true 时使用 typedef 重载它。
  • 我确实喜欢您将默认参数移动到模板专业化中,因此用户不能谎报它们是否为数字。但是,您使用typename 版本而不是bool 是否有原因?我发现 typename 版本更难阅读。例如,与template&lt;typename T, typename U, bool=false&gt; struct MyCommonType {};template&lt;typename T, typename U&gt; struct MyCommonType&lt;T, U, std::is_arithmetic_v&lt;T&gt; &amp;&amp; std::is_arithmetic_v&lt;U&gt;&gt; { typedef std::common_type_t&lt;T, U&gt; type; }; 相比有什么优势?
  • 问和答;我在之前的评论中提出的版本无法编译。
  • @JeffG 似乎专门的非类型模板参数的类型不能依赖于模板参数。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-07-13
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多