模板接受具有一种专业化的 throw 和 nothrow

template accept both throw and nothrow with one specialization

我想写一个模板 class 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 两次,我怎么能做到这一点?

Why I got that error C2057? How can I do it without specializing MyClass twice like I did with IsNoThrow?

我想这个错误是一个 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) 如果您对函数的 return 类型和参数类型不感兴趣(但仅在拦截函数并检测它们是否抛出或不抛出时)只有一个 main class/struct for MyClass(无特化)继承自 IsNoThrow

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

这种方式 MyClass 仅当 T 类型是函数类型时才编译(MyClass<int> 给出编译错误)并继承自 std::true_typestd::false_type 根据 noexcept 值。

#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
 }

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

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,我想(假设您还需要推导 return 和参数类型)你需要两个 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
 }