【问题标题】:if constexpr instead of tag dispatchif constexpr 而不是标签调度
【发布时间】:2017-09-12 06:47:39
【问题描述】:

我想使用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;
   }
};

【问题讨论】:

  • 你不能在类型上“调用”operator==;这是没有意义的。你可以用 boost hana 之类的库做类似的事情:if constexpr (hana::type_c&lt;T&gt; == hana::type_c&lt;std::uint64_t&gt;)。你也可以只使用std::is_same:if constexpr (std::is_same_v&lt;T, std::uint64_t&gt;)
  • 类型不是值,不能这样比较。
  • @Rakete1111 是的,我知道我不能像那样比较它们,这就是问题的全部所在,我应该怎么做......以及为什么要投反对票?
  • @Justin 这对我来说似乎是一个不错的答案,愿意这样发布吗?

标签: c++ templates c++17 if-constexpr


【解决方案1】:

一种方法是定义一个 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";
        }
    }
};

【讨论】:

  • 感谢非常好的答案,如果 T 是指针或引用,则需要 std::decay ,对吧?
  • @0xBADF00 衰减会去除 const、reference 等,因此您可以比较底层类型。
  • @RichardHodges 这对泛型 lambdas 有一定意义,但对于“普通”函数,可以使用模板值匹配规则来停止非法类型的编译。
  • @RichardHodges 甚至只是声明具有所需类型的方法。同上,一个函数式/lambdas 示例可能会更好......
【解决方案2】:

通常,在使用泛型 lambda(也具有泛型参数)的函数式编程中,类型的运行时查询是有意义的。否则简单的答案可能是:只需声明使用“必需”类型或使用类型特征等……回到泛型 lambda 的主题。

/// <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_) ;
      } 
}

我宁愿在along this answer 某处使用std::enable_if,而不是上面的。

但是(如前所述)为了解决 C++17 中的几个通用 lambdas 问题,我会(大胆地)使用我的命名空间 required_types,并进行一些扩展。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-07-21
    • 2012-03-02
    • 2020-12-13
    • 1970-01-01
    • 2010-12-20
    • 1970-01-01
    • 2016-11-13
    • 1970-01-01
    相关资源
    最近更新 更多