【问题标题】:template accept both throw and nothrow with one specialization模板接受一个专业化的 throw 和 nothrow
【发布时间】:2019-01-13 10:48:48
【问题描述】:

我想编写一个模板类MyClass,它接受普通签名和noexcept 签名。例如MyClass<int()>MyClass<int() noexcept>

这是我尝试过的:

template<typename TSignature>
struct IsNoThrow;

template<typename TReturn, typename...TArgs>
struct IsNoThrow<TReturn(TArgs...)> {
    static constexpr bool value = false;
};

template<typename TReturn, typename...TArgs>
struct IsNoThrow<TReturn(TArgs...) noexcept> {
    static constexpr bool value = true;
};

template<typename T, bool = IsNoThrow<T>::value>
class MyClass;


template<bool BNoThrow, typename TReturn, typename...TParams>
class MyClass<TReturn(TParams...) noexcept(BNoThrow), BNoThrow> {
    //VS2017(/std:c++latest) gives error C2057: expected constant expression
};


int main() {
    MyClass<int()> mc;
}

为什么我收到 C2057 错误?如果没有像 IsNoThrow 那样专门化 MyClass 两次,我怎么能做到这一点?

【问题讨论】:

  • MSVC 还不完全支持 C++17。我认为你没有任何追索权。

标签: c++ templates c++17 variadic-templates template-meta-programming


【解决方案1】:

为什么我收到 C2057 错误?如果没有像使用 IsNoThrow 那样专门化 MyClass 两次,我怎么能做到这一点?

我认为错误是 VC 错误,但无论如何,您的解决方案对我来说似乎过于复杂。

我提议

(1) 对于IsNoThrow,从std::true_typestd::false_type 继承(以简化和使用std::integral_constant 中的设施)

template<typename TSignature>
struct IsNoThrow;

template<typename TReturn, typename...TArgs>
struct IsNoThrow<TReturn(TArgs...)> : public std::false_type
 { };

template<typename TReturn, typename...TArgs>
struct IsNoThrow<TReturn(TArgs...) noexcept> : public std::true_type
 { };

或者,也许,简单

template<typename TSignature>
struct IsNoThrow;

template<typename TReturn, typename...TArgs, bool B>
struct IsNoThrow<TReturn(TArgs...) noexcept(B)> : public std::integral_constant<bool, B>
 { };

(2) 如果您对函数的返回类型和参数类型不感兴趣(但只对拦截函数并检测它们是否正在抛出或不抛出)只有 MyClass 的主类/结构(否特化)继承自IsNoThrow

template<typename T>
struct MyClass : public IsNoThrow<T>
{ };

这种方式MyClass 仅在T 类型是函数类型时编译(MyClass&lt;int&gt; 给出编译错误)并根据noexcept 值从std::true_typestd::false_type 继承。

#include <type_traits>

template<typename TSignature>
struct IsNoThrow;

template<typename TReturn, typename...TArgs>
struct IsNoThrow<TReturn(TArgs...)> : public std::false_type
 { };

template<typename TReturn, typename...TArgs>
struct IsNoThrow<TReturn(TArgs...) noexcept> : public std::true_type
 { };

template<typename T>
struct MyClass : public IsNoThrow<T>
 { };

int foo (int)
{ return 0; }

int bar (int) noexcept
{ return 0; }

int main()
 {    
   static_assert( false == MyClass<decltype(foo)>::value );
   static_assert( true  == MyClass<decltype(bar)>::value );
   static_assert( false == MyClass<int(int)>::value );
   static_assert( true  == MyClass<int(int) noexcept>::value ); 

   //MyClass<int> mc; // compilaton error
 }

如果您对返回和参数类型感兴趣,在我看来您需要专业化,并且可能的解决方案是

template<typename T>
struct MyClass;

template<typename TReturn, typename ... TArgs, bool B>
struct MyClass<TReturn(TArgs...) noexcept(B)> : public std::integral_constant<bool, B>
 { };

-- 编辑--

如果您的 VC 编译器在推断 noexcept(B) 中的布尔值时不支持 C++17,我想(鉴于您还需要推断返回和参数类型)您需要两个 MyClass 特化.

但是如果你的问题是你必须复制这个专业的内容,我提出了一个双专业的解决方案,其中第二个继承自第一个:

template<typename T, bool = false>
struct MyClass;

template<typename TReturn, typename ... TArgs, bool B>
struct MyClass<TReturn(TArgs...), B> : public std::integral_constant<bool, B>
 { /* all common member/methods here */ };

template<typename TReturn, typename ... TArgs>
struct MyClass<TReturn(TArgs...) noexcept> 
    : public MyClass<TReturn(TArgs...), true>
 { /* empty: inherhit all from the other specialization */ };

这样你就不需要IsNoThrow,你可以只开发第一个特化:其中的所有成员和方法都继承自另一个特化。

以下是完整的编译示例

#include <type_traits>

template<typename T, bool = false>
struct MyClass;

template<typename TReturn, typename ... TArgs, bool B>
struct MyClass<TReturn(TArgs...), B> : public std::integral_constant<bool, B>
 { 
   /* all common member/methods here */

   static constexpr bool isNoExcept ()
    { return B; }
 };

template<typename TReturn, typename ... TArgs>
struct MyClass<TReturn(TArgs...) noexcept> 
    : public MyClass<TReturn(TArgs...), true>
 { /* empty: inherhit all from the other specialization */ };

int foo (int)
{ return 0; }

int bar (int) noexcept
{ return 0; }

int main()
 { 
   // using value 
   static_assert( false == MyClass<decltype(foo)>::value );
   static_assert( true  == MyClass<decltype(bar)>::value );
   static_assert( false == MyClass<int(int)>::value );
   static_assert( true  == MyClass<int(int) noexcept>::value ); 

   // using isNoExcept() 
   static_assert( false == MyClass<decltype(foo)>::isNoExcept() );
   static_assert( true  == MyClass<decltype(bar)>::isNoExcept() );
   static_assert( false == MyClass<int(int)>::isNoExcept() );
   static_assert( true  == MyClass<int(int) noexcept>::isNoExcept() ); 

   //MyClass<int> mc; // compilaton error
 }

【讨论】:

  • 遗憾的是 MyClass 需要知道返回类型和所有参数类型
  • @JohnSmith - 所以你需要专业知识...给我几分钟。
  • @JohnSmith - 回答得到改进。
  • 我收到了error C2764: 'B': template parameter not used or deducible in partial specialization MyClass&lt;TReturn(TArgs...)&gt;。该错误甚至没有noexcept(B)。也许我应该等到 VS 完全支持 c++17。
  • @JohnSmith - 如果您的问题是两个专业中的代码重复,则答案得到了改进(添加了另一个示例)。
猜你喜欢
  • 1970-01-01
  • 2020-07-18
  • 2020-12-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-12-28
  • 2015-08-01
相关资源
最近更新 更多