简体   繁体   English

使用`void_t`来检测多个继承类型的重复错误

[英]Using `void_t` to detect multiple inheritance type repetition errors

I want to implement a has_no_duplicates<...> type trait that evaluates to std::true_type if the passed variadic type list has no duplicate types. 我想实现一个has_no_duplicates<...>类型特征,如果传递的可变参数类型列表没有重复类型,则该特征的计算结果为std::true_type

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

Let's assume, for the scope of this question, that I want to do that using multiple inheritance. 让我们假设,对于这个问题的范围,我想使用多重继承来做到这一点。

When a class inherits from the same type more than once, an error occurs. 当一个类多次从同一类型继承时,会发生错误。

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{};

I assumed I could've used void_t to "detect" this error, but I couldn't implement a working solution following the code samples from cppreference . 我以为我可以使用void_t来“检测”这个错误,但是我无法在cppreference的代码示例之后实现一个有效的解决方案。

This is what I tried: 这是我试过的:

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 { };

For my third try, I tried delaying the expansion of dup_helper<...> using a wrapper class that took dup_helper as a template template parameter, like wrapper<dup_helper, ...> and expanded it inside of void_t . 对于我的第三次尝试,我尝试使用包装类延迟dup_helper<...>的扩展,该包装类将dup_helper作为模板模板参数,如wrapper<dup_helper, ...>并在void_t内扩展它。

Unfortunately, all my tries resulted in the aforementioned error always preventing compilation. 不幸的是,我的所有尝试导致上述错误始终阻止编译。

I assume this type of error is not detectable as a "substitution failure", but I'd like confirmation. 我认为这种类型的错误不能被视为“替换失败”,但我想要确认。


Is this kind of error actually impossible to detect using void_t ? 使用void_t实际上无法检测到这种错误吗? (Will it always result in a compilation failure?) (它总会导致编译失败吗?)

Is there a way to detect it without causing compilation to fail? 有没有办法检测它而不会导致编译失败? (Or a non- void_t workaround that still makes use of the "multiple inheritance trick")? (或者仍然使用“多重继承技巧”的非void_t变通方法)?

As @Canoninos noted, the problem is that: 正如@Canoninos所说,问题在于:

it isn't the declaration of dup_helper<T, T> which causes an error but its definition [...]. 它不是dup_helper<T, T>的声明,它会导致错误但其定义[...]。

Or, in Standardese, the error occurs outside the "immediate context" ( [temp.deduct] ) of the substitution: 或者,在Standardese中,错误发生在替换的“直接上下文”( [temp.deduct] )之外:

8 - [...] Only invalid types and expressions in the immediate context of the function type and its template parameter types can result in a deduction failure. 8 - [...]只有函数类型及其模板参数类型的直接上下文中的无效类型和表达式才会导致演绎失败。 [ Note: The evaluation of the substituted types and expressions can result in side effects such as the instantiation of class template specializations and/or function template specializations, the generation of implicitly-defined functions, etc. Such side effects are not in the “immediate context” and can result in the program being ill-formed. [ 注意:对替换类型和表达式的评估可能会导致副作用,例如类模板特化和/或函数模板特化的实例化 ,隐式定义函数的生成等。这些副作用不在“立即上下文“并且可能导致程序格式不正确。 end note ] - 结束说明 ]

Here the error occurs while instantiating dup_helper<float, float> so is not in the "immediate context". 这里在实例化 dup_helper<float, float>时发生错误dup_helper<float, float>因此不在“立即上下文”中。

One multiple inheritance trick that's very close to yours involves adding an extra layer of inheritance, by indexing the multiple bases: 一个非常接近你的多重继承技巧涉及通过索引多个基数来添加额外的继承层:

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

This gives us a helper class with a valid definition and that can be instantiated but not cast to its ultimate base classes, because of ambiguity: 这给了我们一个带有有效定义的辅助类,并且可以实例化但不会强制转换为它的最终基类,因为它有歧义:

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

Example . 例子

That's my solution using meta-programming and type-list idiom. 这是我使用元编程和类型列表习语的解决方案。 I use this code as part of my library implementing reflection for C++. 我将此代码用作实现C ++反射的库的一部分。 I think there is no need in void_t or inheritance at all to solve this task. 我认为在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>
{};

DEMO DEMO

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM