[英]Wrapping non type template constants to avoid mixing parameters of same type
我有一個采用非類型模板參數的模板方法。 它具有以下形式:
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
不是普通的結構。 並且在編譯時就知道了構建它所需的值。 這就是為什么我想到使用非類型模板的原因。
#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}>();
另外,您可以允許以任意順序指定參數,而不是在參數/參數類型不匹配時引發錯誤:
#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}>();
這是一種實現語法略有不同的解決方案:
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;
然后,我們可以將“ strong typedefs”定義為繼承自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>
>
{
}
有了更引人注目的界面更改,代碼可以變得更加簡單:
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>{}
);
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.