если constexpr вместо отправки тегов

Я хочу использовать if constexpr вместо отправки тегов, но я не уверен, как его использовать. Пример кода ниже.

template<typename T>
struct MyTag
{
static const int Supported = 0;
};

template<>
struct MyTag<std::uint64_t>
{
static const int Supported = 1;
};

template<>
struct MyTag<std::uint32_t>
{
static const int Supported = 1;
};

class MyTest
{
public:
template<typename T>
void do_something(T value)
{
// instead of doing this
bool supported = MyTag<T>::Supported;

// I want to do something like this
if constexpr (T == std::uint64_t)
supported = true;
}
};

5

Решение

Одним из способов является определение предиката constexpr, который проверяет тип его аргумента, а затем переключение constexpr на результат этого предиката.

Я думаю, что этот способ хорош, потому что он отделяет функциональную логику от логики предусловий.

#include <iostream>
#include <cstddef>
#include <type_traits>

class MyTest
{
public:
template<typename T>
void do_something(T value)
{
// define our predicate
// lambdas are constexpr-if-possible in c++17
constexpr auto is_supported = [](auto&& x) {
if constexpr (std::is_same<std::decay_t<decltype(x)>, std::uint64_t>())
return true;
else
return false;
};

// use the result of the predicate
if constexpr (is_supported(value))
{
std::cout << "supported\n";
}
else
{
std::cout << "not supported\n";
}
}
};

int main()
{
auto t = MyTest();

t.do_something(int(0));
t.do_something(std::uint64_t(0));
t.do_something(double(0));
t.do_something(static_cast<unsigned long>(0));  // be careful with std::uint_xx aliases

}

пример результатов:

not supported
supported
not supported
supported

Другой способ выразить это может быть:

class MyTest
{
public:

template<class T>
static constexpr bool something_possible(T&&)
{
return std::is_same<std::decay_t<T>, std::uint64_t>();
}

template<typename T>
void do_something(T value)
{
// switch behaviour on result of constexpr predicate
if constexpr (something_possible(value))
{
std::cout << "supported\n";
}
else
{
std::cout << "not supported\n";
}
}
};
5

Другие решения

Обычно запрос типов во время выполнения имеет смысл в функциональном программировании с использованием обобщенных лямбд (также с общими аргументами). В противном случае простой ответ может быть следующим: просто объявить с использованием «обязательных» типов или использовать черты типа и т. Д. Вернуться к теме общих лямбда-выражений.

/// <summary>
/// c++ 17 generic lambdas have issues
/// with required types of auto arguments
/// in c++20 this will be fixed with new
/// lambda arguments template declaration syntax
/// until then ...
/// </summary>
namespace required_types
{
template<typename RQ>
inline auto  is_required_type = [](const auto & v_ = 0) constexpr -> bool
{
using T = std::decay_t< decltype(v_) >;
return std::is_same<T, RQ>();
};inline auto is_uint64 = [] ( const auto & v_ = 0 ) constexpr -> bool
{
return is_required_type<std::uint64_t>(v_);
};

} // required_types

namespace {

using namespace required_types;

inline auto tv = [](const char prompt[] = "", const auto & value) {
std::cout << prompt << "\ntype:\t" << typeid(decltype(value)).name() << "\nvalue:\t" << value;
};inline auto make_double_value = [](auto value)
{
if constexpr (is_uint64(value)) {
tv("\n\nDoubling required type (std::uint_64):", value);
return value + value;
}

tv("\n\nWill try to double 'illegal' type", value);
return value + value;
};

}

некоторое использование

        // call with 'legal' aka required type
std::uint64_t u42 = 42u;
auto double_value_2 = make_double_value(u42);
tv("\nResult:", double_value_2);

// call with some 'illegal' types also works
auto double_value = make_double_value(42u);
tv("\nResult:", double_value);

std::string one{"--ONE--"};
auto double_value_3 = make_double_value(one);
tv("\nResult:", double_value_3 );

Конечно, если кто-то горячо не согласен с моим вступлением, он все равно может использовать мои «required_types»:

template<typename T>
void some_proc ( const T && val_ ) {
using namespace required_types;
if constexpr ( is_required_type<std::uint64_t>(val_) ) {
do_something_with_uint64 (val_) ;
}
}

Вместо вышеупомянутого я бы предпочел использовать std :: enable_if, где-нибудь по этому ответу.

Но (как уже упоминалось) для решения нескольких общих проблем с лямбдами в C ++ 17 я бы (смело) использовал свое пространство имен required_types с некоторыми расширениями.

0

По вопросам рекламы [email protected]