模板接受一个专业化的throw和nothrow

时间:2019-01-13 10:48:48

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

我想编写一个模板类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?像我对MyClass所做的那样,如何不专门对IsNoThrow进行两次操作,怎么办?

1 个答案:

答案 0 :(得分:1)

  

为什么我收到该错误C2057?像我使用IsNoThrow一样,如何在不两次专门化MyClass的情况下做到这一点?

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

我提议

(1)从IsNoThrowstd::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
 { };

或者也许是simpy

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

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

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和arguments类型)则需要两个{ 1}}专业化。

但是,如果您的问题是您必须重复此专业化的内容,那么我建议采用两种专业化的解决方案,其中第二个从第一个继承过来:

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