簡體   English   中英

用於檢查所有模板參數類型是否唯一的編譯時 C++ 函數

[英]Compile-time C++ function to check whether all template argument types are unique

有一個很好的問題( 在 requires 子句中不允許哪些替換失敗? )提出了下一個問題。

需要編寫一個編譯時函數template<typename... Ts> constexpr bool allTypesUnique()如果所有參數類型都是唯一的,它將返回true ,否則返回false 並且限制不是成對比較參數類型 不幸的是,答案僅解釋了為什么不能用某些特定方法實現這樣的功能。

我認為可以使用多重繼承來實現解決方案。 這個想法是讓一個類繼承自多個類: Ts每個類型T一個。 每個這樣的類都定義了一個帶有依賴於T的簽名的虛函數。 如果在Ts多次找到某個T則子類中的函數f將覆蓋基類中的函數,並且可以檢測到:

template<typename> struct A{};

template<typename T, typename... Ts>
struct B : B<Ts...> {
    using B<Ts...>::f;
    constexpr virtual void f(A<T>, bool & unique) { if( ++count > 1 ) unique = false; }
    int count = 0;
};

template<typename T>
struct B<T> {
    constexpr virtual void f(A<T>, bool & unique) { if( ++count > 1 ) unique = false; }
    int count = 0;
};

template<typename... Ts>
constexpr bool allTypesUnique() {
    B<Ts...> b;
    bool res = true;
    ( b.f( A<Ts>{}, res ), ... );
    return res;
}

int main() {
    static_assert( allTypesUnique<void>() );
    static_assert( allTypesUnique<void, int&>() );
    static_assert( !allTypesUnique<int&, int&>() );
    static_assert( allTypesUnique<char, short, int>() );
    static_assert( !allTypesUnique<char, short, char>() );
}

演示: https : //gcc.godbolt.org/z/8jhnE7P11

只是好奇,解決方案是否正確,是否有針對此問題的更簡單的解決方案?

如果您根據每個給定的類型使用虛擬基類,那么對於結果類中的每個唯一類型,您將獲得准確的一個基類實例。 如果給定類型的數量是生成的基類的數量,則每種類型都是唯一的。 您可以通過其大小“測量”生成的基類的數量,但必須注意您有一個 vtable 指針,其中的大小取決於實現。 因此,每個生成的類型應該足夠大以隱藏對齊問題。

順便說一句:它也適用於引用類型。


template < typename T> struct AnyT { char i[128]; };

template < typename FIRST, typename ... T>
struct CheckT: virtual AnyT<FIRST>, virtual CheckT<T...> { };

template < typename FIRST >
struct CheckT<FIRST>: virtual AnyT<FIRST> {};


template < typename ... T>
constexpr bool allTypesUnique()
{
    using T1 = CheckT<int>;
    using T2 = CheckT<bool, int>;

    constexpr std::size_t s1 = sizeof( T1 );
    constexpr std::size_t s2 = sizeof( T2 );
    constexpr std::size_t diff = s2 - s1; 
    constexpr std::size_t base = s1 - diff;
    constexpr std::size_t measure = sizeof( CheckT< T...> );

    return !((sizeof...(T)*diff+base) - measure);
}


int main() {
    static_assert( allTypesUnique<void>() );
    static_assert( allTypesUnique<void, int>() );
    static_assert( !allTypesUnique<void, void>() );
    static_assert( allTypesUnique<char, short, int>() );
    static_assert( !allTypesUnique<char, short, char>() );
}

演示

在支持類大小優化的編譯器中,可以通過 C++20 屬性[[no_unique_address]]為空成員獲得更簡單的解決方案。 如果所有類的空成員都有不同的類型,那么它的sizeof將是 1。如果某些成員類型是重復的,那么它們不能共享相同的地址並且sizeof將大於 1。

解決代碼:

template<typename> struct A{};

template<typename T, typename... Ts>
struct B : B<Ts...> {
    [[no_unique_address]] A<T> a;
};

template<typename T>
struct B<T> {
    [[no_unique_address]] A<T> a;
};

template<typename... Ts>
constexpr bool allTypesUnique() {
    if constexpr (sizeof...(Ts) <= 1 )
        return true;
    else
        return sizeof(B<Ts...>) == 1;
}

int main() {
    static_assert( allTypesUnique<void>() );
    static_assert( allTypesUnique<void, int&>() );
    static_assert( !allTypesUnique<int&, int&>() );
    static_assert( allTypesUnique<char, short, int>() );
    static_assert( !allTypesUnique<char, short, char>() );
}

演示: https : //gcc.godbolt.org/z/577EP1774

我認為您可以嘗試詳細說明使用編譯時類型 id:

#include <algorithm>

namespace detail
{
    template <typename T>
    struct type_id
    {
        constexpr static int value{};
    };
}

template <typename ... T>
constexpr bool all_types_unique()
{
    if constexpr (!sizeof...(T))
        return true;

    const int *ids[] = {
        &detail::type_id<T>::value...
    };
    auto iterBegin = ids,
         iterEnd = std::next(ids, sizeof...(T));
    std::sort(iterBegin, iterEnd);

    while (++iterBegin != iterEnd)
        if (*iterBegin == *std::prev(iterBegin))
            return false;

    return true;
}

int main()
{
    static_assert(all_types_unique<int>());
    static_assert(all_types_unique<int, double>());
    static_assert(!all_types_unique<int, int>());

    return 0;
}

https://godbolt.org/z/8GeT8zbcc

這用 MSVC 編譯,但是 Clang、MinG64 和最新的 GCC 不能編譯( std::sort給出錯誤)。
這會給你n*log(n)復雜度。

可以構造一個constexpr函數來回答這個問題:

template <class T, class... Ts>
constexpr bool unique_types()
{
    if constexpr (sizeof...(Ts)) {
        return !(std::is_same_v<T,Ts> || ...) && unique_types<Ts...>();
    }
    else {
        return true;
    }
}

演示

上面的代碼是等價的編譯時間

for (int i(0); i < n; ++i)
  for (int j(i + 1); j < n; ++j)
    { /* check type[i] vs type[j] */ }

如果您想要比較中的任何變化(例如考慮intint&相同)只需修改std::is_same_v (例如std::is_same_v<std::decay_t<T>, std::decay_t<Ts>>

暫無
暫無

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

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