簡體   English   中英

使用`void_t`來檢測多個繼承類型的重復錯誤

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

我想實現一個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_helper作為模板模板參數,如wrapper<dup_helper, ...>並在void_t內擴展它。

不幸的是,我的所有嘗試導致上述錯誤始終阻止編譯。

我認為這種類型的錯誤不能被視為“替換失敗”,但我想要確認。


使用void_t實際上無法檢測到這種錯誤嗎? (它總會導致編譯失敗嗎?)

有沒有辦法檢測它而不會導致編譯失敗? (或者仍然使用“多重繼承技巧”的非void_t變通方法)?

正如@Canoninos所說,問題在於:

它不是dup_helper<T, T>的聲明,它會導致錯誤但其定義[...]。

或者,在Standardese中,錯誤發生在替換的“直接上下文”( [temp.deduct] )之外:

8 - [...]只有函數類型及其模板參數類型的直接上下文中的無效類型和表達式才會導致演繹失敗。 [ 注意:對替換類型和表達式的評估可能會導致副作用,例如類模板特化和/或函數模板特化的實例化 ,隱式定義函數的生成等。這些副作用不在“立即上下文“並且可能導致程序格式不正確。 - 結束說明 ]

這里在實例化 dup_helper<float, float>時發生錯誤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

例子

這是我使用元編程和類型列表習語的解決方案。 我將此代碼用作實現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>
{};

DEMO

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM