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 将取消该功能。