【问题标题】:Wrapping non type template constants to avoid mixing parameters of same type包装非类型模板常量以避免混合相同类型的参数
【发布时间】:2018-11-27 18:00:32
【问题描述】:

我有一个采用非类型模板参数的模板方法。 它具有以下形式:

template <long long connectionTimeout, long long sendTimeout, bool autoAck>
void create() { ... }

它是另一个头文件中的实用函数,而在调用者的代码中让我烦恼的是常量没有类型。

意思,而不是这种调用方式:

create<1, 2, true>();

我更喜欢以下:

create<
    connection_timeout {1},
    send_timeout {2},
    auto_ack {true}
>();

使用create 函数保证不能传递send_timeout 而不是connection_timeout

然而,我开始编写概念证明时存在一些空白。我想让它与 C++11/14 一起工作。但是,我必须使用 C++17 构造(cf 代码)才能使事情工作到现在。话虽如此,我不介意 C++17 解决方案来了解是否可以做到这一点。

下面缺少的是类型匹配的编译时检查。但是,主要的语法是我想要的。

#include <iostream>
#include <string>

template <typename T, T userSpecifiedValue>
struct compile_time_constant_wrapper
{
   using type = T;
   static const T defaultValue = userSpecifiedValue;

   constexpr operator T() const
   {
       return value;
   }

   T value = defaultValue;
};

using connection_timeout = compile_time_constant_wrapper<long long, 5000>;
using send_timeout = compile_time_constant_wrapper<long long, 10>;
using auto_ack = compile_time_constant_wrapper<bool, false>;

struct ComplicatedToBuild
{
    long long connectionTimeout;
    long long sendTimeout;
    bool autoAck;
};

template <typename T, 
          long long connectionTimeout = connection_timeout {} /*-std=c++17*/,
          long long sendTimeout = send_timeout {} /*-std=c++17*/,
          bool autoAck = auto_ack {} /*-std=c++17*/>
struct create
{
    operator T() const
    {
        return T{connectionTimeout, sendTimeout, autoAck};
    }
};

std::ostream& operator<<(std::ostream& out, const ComplicatedToBuild& complicated)
{
    out << "connection timeout = " << complicated.connectionTimeout << ", "
        << "send timeout = " << complicated.sendTimeout << ", "
        << "auto ack = " << complicated.autoAck;
    return out;
}

int main()
{
    ComplicatedToBuild defaultValuesCase = create<ComplicatedToBuild>();
    std::cout << "defaultValuesCase: " << defaultValuesCase << std::endl;

    ComplicatedToBuild customizedCase = create<
           ComplicatedToBuild,
           connection_timeout {2500},
           send_timeout {5},
           auto_ack {true}
    >();
    std::cout << "customizedCase: " << customizedCase << std::endl;

    ComplicatedToBuild compilationErrorCase = create<
           ComplicatedToBuild,
           send_timeout {5},
           connection_timeout {2500},
           auto_ack {true}
    >();
}

在我的例子中,ComplicatedToBuild 类不是一个简单的结构。并且构建它所需的值在编译时是已知的。这就是我想到使用非类型模板的原因。

【问题讨论】:

  • 有关于“强类型整数”的问题。 stackoverflow.com/search?q=strongly+typed+integer。希望您没有重新发明轮子。
  • 或者使用枚举作为包装整数的强类型?我当然建议使用带值的枚举来包装布尔值。在源代码中几乎没有比Setup(true, false, 1, false, 55000) 更糟糕的了,比Setup(Screen::ON, Mike::OFF, Brightness {1}, Camera::OFF, Distance{ 55000}) 好很多

标签: c++ c++11 templates c++14 c++17


【解决方案1】:
#include <type_traits>    

enum class connection_timeout : long long {};
enum class send_timeout : long long {};
enum class auto_ack : bool {};

struct ComplicatedToBuild
{
    long long connectionTimeout;
    long long sendTimeout;
    bool autoAck;
};

template <typename T
        , connection_timeout connectionTimeout = connection_timeout{5000}
        , send_timeout sendTimeout = send_timeout{10}
        , auto_ack autoAck = auto_ack{false}>
T create()
{
    return {std::underlying_type_t<connection_timeout>(connectionTimeout)
          , std::underlying_type_t<send_timeout>(sendTimeout)
          , std::underlying_type_t<auto_ack>(autoAck)};
}

create<ComplicatedToBuild,
         connection_timeout{2500},
         send_timeout{5}, 
         auto_ack{true}>();

DEMO


或者,您可以允许以任意顺序指定参数,而不是引发参数/参数类型不匹配的错误:

#include <tuple>

template <typename T, auto Arg, auto... Args>
T create()
{
    auto t = std::make_tuple(Arg, Args...);
    return {
         std::underlying_type_t<connection_timeout>(std::get<connection_timeout>(t))
       , std::underlying_type_t<send_timeout>(std::get<send_timeout>(t))
       , std::underlying_type_t<auto_ack>(std::get<auto_ack>(t))
    };
}

template <typename T>
T create()
{
    return create<T, connection_timeout{5000}, send_timeout{10}, auto_ack{false}>();
}

create<ComplicatedToBuild,
         connection_timeout{2500},
         send_timeout{5},
         auto_ack{true}>();    

create<ComplicatedToBuild,
         auto_ack{true},
         send_timeout{5},
         connection_timeout{2500}>();

DEMO 2

【讨论】:

    【解决方案2】:

    这是一个实现稍微不同语法的解决方案:

    create<
        connection_timeout<1>,
        send_timeout<2>,
        auto_ack<true>
    >();
    

    首先,我们需要一个is_instantiation_of 助手:

    template <typename T, template <auto...> class C>
    struct is_instantiation_of_impl : std::false_type { };
    
    template <auto... Ts, template <auto...> class C>
    struct is_instantiation_of_impl<C<Ts...>, C>  : std::true_type { };
    
    template <typename T, template <auto...> class C>
    constexpr bool is_instantiation_of = is_instantiation_of_impl<T, C>::value;
    

    然后,我们可以将“强类型定义”定义为继承自 std::integral_constant 的类:

    template <long long X>
    struct connection_timeout : std::integral_constant<long long, X> { };
    
    template <long long X>
    struct send_timeout : std::integral_constant<long long, X> { };
    
    template <bool X>
    struct auto_ack : std::integral_constant<bool, X> { };
    

    最后,我们的界面将如下所示:

    template <typename ConnectionTimeout,
              typename SendTimeout,
              typename AutoAck>
    auto create()
        -> std::enable_if_t<
            is_instantiation_of<ConnectionTimeout, connection_timeout> 
         && is_instantiation_of<SendTimeout, send_timeout>
         && is_instantiation_of<AutoAck, auto_ack>
        >
    {
    }
    

    live example on godbolt.org


    通过更显着的界面变化,代码可以简单得多:

    template <long long A, long long B, bool C>
    auto create(connection_timeout<A>, send_timeout<B>, auto_ack<C>)
    {
    }
    
    int main()
    {
        create(
            connection_timeout<1>{},
            send_timeout<2>{},
            auto_ack<true>{}
        );
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-08-19
      • 2016-06-24
      • 1970-01-01
      • 2018-12-05
      • 1970-01-01
      • 2021-12-28
      相关资源
      最近更新 更多