Шаблон принимаю как бросок, так и nothrow с одной специализацией - PullRequest
0 голосов
/ 13 января 2019

Я хочу написать шаблон класса MyClass, который принимает как обычную, так и нет, кроме подписи. Например 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 голосов
/ 13 января 2019

Почему я получил эту ошибку C2057? Как я могу сделать это, не специализируя MyClass дважды, как я это делал с IsNoThrow?

Полагаю, ошибка связана с ошибкой VC, но в любом случае ваше решение мне кажется слишком сложным.

Я предлагаю

(1) наследование, для IsNoThrow, от std::true_type и std::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<int> дает ошибку компиляции) и наследуется от std::true_type или от std::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 не поддерживает C ++ 17 в момент вывода логического значения в noexcept(B), я полагаю, что (учитывая, что вам нужно также вывести типы возврата и аргументы), вам нужно две специализации 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
 }
...