8

我想实现一个has_no_duplicates<...>类型特征来评估std::true_type传递的可变参数类型列表是否没有重复类型。

static_assert(has_no_duplicates<int, float>{}, "");
static_assert(!has_no_duplicates<float, float>{}, "");

让我们假设,对于这个问题的范围,我想使用多重继承来做到这一点。

当一个类多次继承同一个类型时,就会发生错误。

template<class T> 
struct type { };

template<class... Ts>
struct dup_helper : type<Ts>... { };

// No errors, compiles properly.
dup_helper<int, float> ok{};

// Compile-time error: 
// base class 'type<float>' specified more than once as a direct base class
dup_helper<float, float> error{};

我以为我可以用来void_t“检测”这个错误,但是我无法按照 cppreference 中的代码示例实现有效的解决方案。

这是我尝试过的:

template<class, class = void>
struct is_valid 
    : std::false_type { };

// First try:
template<class T>
struct is_valid<T, std::void_t<decltype(T{})>> 
    : std::true_type { };

// Second try:
template<class T>
struct is_valid<T, std::void_t<T>> 
    : std::true_type { };

对于我的第三次尝试,我尝试延迟使用作为模板模板参数dup_helper<...>的包装类的扩展,例如并在.dup_helperwrapper<dup_helper, ...>void_t

不幸的是,我所有的尝试都导致上述错误总是阻止编译。

我认为这种类型的错误不能检测为“替换失败”,但我想确认一下。


这种错误实际上是无法检测到的void_t吗?(会不会总是导致编译失败?)

有没有办法在不导致编译失败的情况下检测它?void_t(或者仍然使用“多重继承技巧”的非解决方法)?

4

2 回答 2

7

正如@Canoninos 所指出的,问题在于:

它不是dup_helper<T, T>导致错误的声明,而是它的定义[...]。

或者,在 Standardese 中,错误发生在替换的“直接上下文”([temp.deduct])之外:

8 - [...] 只有在函数类型及其模板参数类型的直接上下文中的无效类型和表达式可能导致推导失败。[注意:替换类型和表达式的评估可能会导致副作用,例如类模板特化和/或函数模板特化的实例化,隐式定义函数的生成等。此类副作用不在“立即上下文”,并可能导致程序格式错误。——尾注]

此处在实例化 dup_helper<float, float>时发生错误,因此不在“即时上下文”中。

一个与您非常接近的多重继承技巧涉及通过索引多个基来添加额外的继承层:

helper<<0, 1>, <float, float>>        
             +            
        +----+----+       
        v         v       
 ix<0, float>  ix<1, float>
        +         +       
        v         v       
     t<float>  t<float>   

这为我们提供了一个具有有效定义的帮助类,并且可以实例化但不能转换为它的最终基类,因为存在歧义:

static_cast<t<float>>(helper<...>{});  // Error, SFINAE-usable

例子

于 2015-08-14T19:43:23.143 回答
0

这是我使用元编程和类型列表习语的解决方案。我将此代码用作实现 C++ 反射的库的一部分。我认为根本不需要 void_t 或继承来解决这个任务。

template <typename ...Args>
struct type_list
{};

using empty_list = type_list<>;

// identity
template<typename T>
struct identity
{
    using type = T;
};

// is_typelist
template<typename T>
struct is_typelist: std::false_type
{};

template<typename ...Args>
struct is_typelist<type_list<Args...>>: std::true_type
{};

template<typename T>
struct check_typelist
{
    using type = void;
    static constexpr void *value = nullptr;
    static_assert(is_typelist<T>::value, "T is not a type_list!");
};

// indexof
namespace internal {

template<typename T, typename V, std::int64_t index>
struct typelist_indexof_helper: check_typelist<T>
{};

template<typename H, typename ...T, typename V, std::int64_t index>
struct typelist_indexof_helper<type_list<H, T...>, V, index>:
        std::conditional<std::is_same<H, V>::value,
            std::integral_constant<std::int64_t, index>,
            typelist_indexof_helper<type_list<T...>, V, index + 1>
        >::type
{};

template<typename V, std::int64_t index>
struct typelist_indexof_helper<empty_list, V, index>: std::integral_constant<std::int64_t, -1>
{};

}

template<typename T, typename V>
using typelist_indexof = ::internal::typelist_indexof_helper<T, V, 0>;

template<typename T, typename V>
struct typelist_exists: std::integral_constant<bool, typelist_indexof<T, V>::value >= 0>
{};

// remove_duplicates
namespace internal {

template<typename P, typename T>
struct typelist_remove_duplicates_helper: check_typelist<T>
{};

template<typename ...P, typename H, typename ...T>
struct typelist_remove_duplicates_helper<type_list<P...>, type_list<H, T...>>:
        std::conditional<typelist_exists<type_list<T...>, H>::value,
            typelist_remove_duplicates_helper<type_list<P...>, type_list<T...>>,
            typelist_remove_duplicates_helper<type_list<P..., H>, type_list<T...>>
        >::type
{};

template<typename ...P>
struct typelist_remove_duplicates_helper<type_list<P...>, empty_list>: identity<type_list<P...>>
{};

}

template<typename T>
using typelist_remove_duplicates = ::internal::typelist_remove_duplicates_helper<empty_list, T>;


template<typename ...Args>
struct has_no_duplicates: std::integral_constant<bool, std::is_same<type_list<Args...>,
                                                                    typename typelist_remove_duplicates<type_list<Args...>>::type>::value>
{};

演示

于 2015-08-14T20:44:39.803 回答