C++11 检查两组可变参数模板参数是否匹配
C++11 Check two sets of variadic template arguments match
这个问题与我之前提出的关于在 C++11 中实现类似于 Qt signal/slots 的问题有关。
考虑以下(非常简化的信号调度器,在这个例子中没有任何用处,它只是为了演示pattern/problem):
template< typename... TYPES >
class Signal
{
public:
Signal() = default;
~Signal() = default;
template< typename... PARAMETERS >
void broadcast( PARAMETERS &&... p )
{
// static_assert to confirm PARAMETERS can map to TYPES
}
};
这工作得很好,但在实践中存在一些不需要的类型转换。例如;
// acceptable use.
Signal< int, unsigned, float, char >().broadcast( 1, 2u, 0.f, 'a' );
// should fail compilation, first parameter is a float, 4th is an int.
Signal< int, unsigned, float, char >().broadcast( 0.f, 0, 0.f, 0 );
// acceptable use, first parameter is const, but it's convertible.
const int i = 3;
Signal< int, unsigned, float, char >().broadcast( i, 2u, 0.f, 'a');
// acceptable use, first parameter is const &, but it's convertible.
const int & j = i;
Signal< int, unsigned, float, char >().broadcast( j, 2u, 0.f, 'a');
不应该有静默的 float 到 int 的转换。在这种情况下,const/const & 的转换应该是可能的(TYPES 的格式不应包含 const 或 &,因为所有数据都应按值传递)。
我想阻止发生这种不需要的类型转换的编译。我想将 TYPES 和 PARAMETERS 包装在元组中,遍历元组并确认给定元组参数索引中的每种类型都匹配(包括使用 std::decay
),但后来我看不到这样做的方法在编译时,它可以进入 static_assert
.
作为参考,选择的编译器是 clang(最新的 OS X 7.3 (clang-703.0.31))和 vc14。
我想做的事情是否可行,如果可行,有人可以提供任何指示吗?
这是我很快想出的元程序。它有点粗糙,但可以用更好的方式实现。您可能应该在元程序中使用衰减类型 (std::decay) 以获得正确的结果。
#include <iostream>
#include <type_traits>
template <typename... T> struct param_pack {};
template <typename, typename> struct is_all_same_impl;
template <>
struct is_all_same_impl<param_pack<>, param_pack<>>
{
static bool const value = true;
};
template <typename T, typename S, typename... Rest, typename... SRest>
struct is_all_same_impl<param_pack<T, Rest...>, param_pack<S, SRest...>>
{
static bool const value = false;
};
template <typename T, typename... Rest, typename... SRest>
struct is_all_same_impl<param_pack<T, Rest...>, param_pack<T, SRest...>>
{
static bool const value = is_all_same_impl<param_pack<Rest...>, param_pack<SRest...>>::value;
};
template <typename, typename>
struct is_all_same;
template <typename... FSet, typename... SSet>
struct is_all_same<param_pack<FSet...>, param_pack<SSet...>>: is_all_same_impl<param_pack<FSet...>, param_pack<SSet...>> {};
int main() {
std::cout << is_all_same<param_pack<int, char, float>, param_pack<int, char, int>>::value << std::endl;
return 0;
}
更新 :: 更简单的版本
template <typename... T> struct param_pack {};
int main() {
std::cout << std::is_same<param_pack<int, float, int>, param_pack<int,float,int>>::value << std::endl;
return 0;
}
所以你可以这样做:
static_assert( is_same<param_pack<Args...>, param_pack<std::decay_t<Dargs>...>>::value, "Parameters do not sufficiently match." );
使用(再次)来自 Columbo 的 :
template <bool...> struct bool_pack;
template <bool... v>
using all_true = std::is_same<bool_pack<true, v...>, bool_pack<v..., true>>;
template <class... Args>
struct Signal {
template <class... Dargs, class = typename std::enable_if<all_true<
std::is_same<Args, typename std::decay<Dargs>::type>{}...
>{}>::type>
void broadcast(Dargs &&...) {}
};
如果参数不完全匹配,此 SFINAE 将取消该功能。
这个问题与我之前提出的关于在 C++11 中实现类似于 Qt signal/slots 的问题有关。
考虑以下(非常简化的信号调度器,在这个例子中没有任何用处,它只是为了演示pattern/problem):
template< typename... TYPES >
class Signal
{
public:
Signal() = default;
~Signal() = default;
template< typename... PARAMETERS >
void broadcast( PARAMETERS &&... p )
{
// static_assert to confirm PARAMETERS can map to TYPES
}
};
这工作得很好,但在实践中存在一些不需要的类型转换。例如;
// acceptable use.
Signal< int, unsigned, float, char >().broadcast( 1, 2u, 0.f, 'a' );
// should fail compilation, first parameter is a float, 4th is an int.
Signal< int, unsigned, float, char >().broadcast( 0.f, 0, 0.f, 0 );
// acceptable use, first parameter is const, but it's convertible.
const int i = 3;
Signal< int, unsigned, float, char >().broadcast( i, 2u, 0.f, 'a');
// acceptable use, first parameter is const &, but it's convertible.
const int & j = i;
Signal< int, unsigned, float, char >().broadcast( j, 2u, 0.f, 'a');
不应该有静默的 float 到 int 的转换。在这种情况下,const/const & 的转换应该是可能的(TYPES 的格式不应包含 const 或 &,因为所有数据都应按值传递)。
我想阻止发生这种不需要的类型转换的编译。我想将 TYPES 和 PARAMETERS 包装在元组中,遍历元组并确认给定元组参数索引中的每种类型都匹配(包括使用 std::decay
),但后来我看不到这样做的方法在编译时,它可以进入 static_assert
.
作为参考,选择的编译器是 clang(最新的 OS X 7.3 (clang-703.0.31))和 vc14。
我想做的事情是否可行,如果可行,有人可以提供任何指示吗?
这是我很快想出的元程序。它有点粗糙,但可以用更好的方式实现。您可能应该在元程序中使用衰减类型 (std::decay) 以获得正确的结果。
#include <iostream>
#include <type_traits>
template <typename... T> struct param_pack {};
template <typename, typename> struct is_all_same_impl;
template <>
struct is_all_same_impl<param_pack<>, param_pack<>>
{
static bool const value = true;
};
template <typename T, typename S, typename... Rest, typename... SRest>
struct is_all_same_impl<param_pack<T, Rest...>, param_pack<S, SRest...>>
{
static bool const value = false;
};
template <typename T, typename... Rest, typename... SRest>
struct is_all_same_impl<param_pack<T, Rest...>, param_pack<T, SRest...>>
{
static bool const value = is_all_same_impl<param_pack<Rest...>, param_pack<SRest...>>::value;
};
template <typename, typename>
struct is_all_same;
template <typename... FSet, typename... SSet>
struct is_all_same<param_pack<FSet...>, param_pack<SSet...>>: is_all_same_impl<param_pack<FSet...>, param_pack<SSet...>> {};
int main() {
std::cout << is_all_same<param_pack<int, char, float>, param_pack<int, char, int>>::value << std::endl;
return 0;
}
更新 :: 更简单的版本
template <typename... T> struct param_pack {};
int main() {
std::cout << std::is_same<param_pack<int, float, int>, param_pack<int,float,int>>::value << std::endl;
return 0;
}
所以你可以这样做:
static_assert( is_same<param_pack<Args...>, param_pack<std::decay_t<Dargs>...>>::value, "Parameters do not sufficiently match." );
使用(再次)来自 Columbo 的
template <bool...> struct bool_pack;
template <bool... v>
using all_true = std::is_same<bool_pack<true, v...>, bool_pack<v..., true>>;
template <class... Args>
struct Signal {
template <class... Dargs, class = typename std::enable_if<all_true<
std::is_same<Args, typename std::decay<Dargs>::type>{}...
>{}>::type>
void broadcast(Dargs &&...) {}
};
如果参数不完全匹配,此 SFINAE 将取消该功能。