9

的模板專業化,我專門爲我的類型std::common_type。我定義了以下專業化:接受所有版本的const/volatile資格和&vs &&

common_type<my_type, my_type> 

而且一切都很好。然後有人來,並呼籲std::common_type<my_type, my_type &>。如果您傳遞引用而不是引用(因爲它在類型上調用std::decay),默認版本的作用相同。但是,它不會推遲到std::common_type的非參考版本,我需要正確工作。難道還有比不得不做這樣的事情(留出右值引用爲const爲簡單起見)更好的方式:

common_type<my_type, my_type> 
common_type<my_type, my_type &> 
common_type<my_type, my_type const &> 
common_type<my_type, my_type volatile &> 
common_type<my_type, my_type const volatile &> 
common_type<my_type, my_type &&> 
common_type<my_type, my_type volatile &&> 
common_type<my_type &, my_type> 
common_type<my_type const &, my_type> 
common_type<my_type volatile &, my_type> 
common_type<my_type const volatile &, my_type> 
common_type<my_type &&, my_type> 
common_type<my_type volatile &&, my_type> 
common_type<my_type &, my_type &> 
common_type<my_type &, my_type const &> 
common_type<my_type &, my_type volatile &> 
... 

肯定有更好的辦法?據我統計,這是49個可能版本,如果我們忽略const &&const volatile &&

注:my_type實際上是一類模板本身,所以在specialize實際上看起來更像

template<intmax_t lhs_min, intmax_t lhs_max, intmax_t rhs_min, intmax_t rhs_max> 
class common_type<my_type<lhs_min, lhs_max>, my_type<rhs_min, rhs_max>> 

其結果爲my_type<min(lhs_min, rhs_min), max(lhs_max, rhs_max)>

如果我完全控制主模板定義,解決方案將非常簡單,但我顯然無法更改std::common_type

+0

你看過你的標準庫的'std :: common_type'實現了嗎? – Casey

+0

這裏的實現沒有太大的自由。它必須調用'std :: decay'(或者有一個實現與這樣做不可區分)。果然,這就是gcc 4.8.2所做的那樣 –

+0

它暗含着某種暗示,但只是爲了確保:你不能使用與「std :: common_type」不同的東西,即改變「call site」?你不能定義SFINAE保護的轉換運算符模板來執行'my_type '「自然」嗎? – dyp

回答

2

據我所知,你不需要完全專門化二元的雙方common_type。這允許將一側的專業化數量減少到12個。如果您只需要my_typemy_type專業化之間的通用類型,那麼專門研究一邊就足夠了。否則,你不得不將它們克隆在右邊,產生24個專業化。

struct my_type; 
struct unique_t; 

#include <type_traits> 

template<class L, class R, class = void> 
struct mytype_common_type 
{ 
    // not many specializations are required here, 
    // as you can use std::decay and don't have to use "Exact Matches" 
    using type = unique_t; 
}; 

namespace std 
{ 
    template<class T> struct common_type<my_type, T> 
    : mytype_common_type<my_type, T> {}; 
    template<class T> struct common_type<my_type const, T> 
    : mytype_common_type<my_type, T> {}; 
    template<class T> struct common_type<my_type volatile, T> 
    : mytype_common_type<my_type, T> {}; 
    template<class T> struct common_type<my_type const volatile, T> 
    : mytype_common_type<my_type, T> {}; 

    template<class T> struct common_type<my_type&, T> 
    : mytype_common_type<my_type, T> {}; 
    template<class T> struct common_type<my_type const&, T> 
    : mytype_common_type<my_type, T> {}; 
    template<class T> struct common_type<my_type volatile&, T> 
    : mytype_common_type<my_type, T> {}; 
    template<class T> struct common_type<my_type const volatile&, T> 
    : mytype_common_type<my_type, T> {}; 

    template<class T> struct common_type<my_type&&, T> 
    : mytype_common_type<my_type, T> {}; 
    template<class T> struct common_type<my_type const&&, T> 
    : mytype_common_type<my_type, T> {}; 
    template<class T> struct common_type<my_type volatile&&, T> 
    : mytype_common_type<my_type, T> {}; 
    template<class T> struct common_type<my_type const volatile&&, T> 
    : mytype_common_type<my_type, T> {}; 
} 

template<class T> 
using Decay = typename std::decay<T>::type; 

int main() 
{ 
    static_assert(std::is_same<unique_t, 
        std::common_type<my_type const volatile&&, int>::type 
        >{}, "!"); 
} 
+0

我沒有看到我如何在右側複製它,實際上。如果我定義'std :: common_type '和'std :: common_type ',那會不會導致模棱兩可的專業化? –

+1

@DavidStone你是對的,如果你只是使用了專業化'common_type '和'common_type ',那就不明確了。但是,您可以通過在右側使用'enable_if'來解決該問題:'template struct common_type :: type,my_type> {},my_type> ::鍵入> :mytype_common_type {};' – dyp

1

我建議沒有CV-和REF-預選賽, 寫專業化和使用包裝周圍std::common_type<>,像這樣:

template <typename T> 
using decay_t = typename std::decay<T>::type; 

/* Our wrapper which passes decayed types to std::common_type<>. */ 
template <typename... T> 
using CommonType = std::common_type<decay_t<T>...>; 

namespace std { 

    /* Specialization for my_type<>. */ 
    template <intmax_t lhs_min, intmax_t lhs_max, 
      intmax_t rhs_min, intmax_t rhs_max> 
    struct common_type<my_type<lhs_min, lhs_max>, 
        my_type<rhs_min, rhs_max>> { 
    using type = /* ... */; 
    }; 

} // std 

現在已經有專長的std::chrono::durationstd::chrono::time_point只專注而不簡歷 - 和ref-qualifiers。

但是,只有在未指定cv-和ref-限定符時纔會使用這些特殊化。 這不是顯而易見的是,不使用它們,因爲,

static_assert(
    std::is_same<std::common_type<const std::chrono::milliseconds, 
           std::chrono::microseconds &&>::type, 
       std::chrono::microseconds>::value, ""); 

的作品就好了。

我很困惑,直到我意識到它不是如何使用專業化。通用實施的std::common_type<>上的if-else表達式使用decltype(),像這樣:

template <typename Lhs, typename Rhs> 
using common_type_impl_t = 
    decay_t<decltype(true ? std::declval<Lhs>() : std::declval<Rhs>())>; 

注意:它使用SFINAE挑這一個,如果它是成功的,否則離開undefined類型。現在

我們可以測試,這實際上是測試它適用於std::chrono::duration

static_assert(
    std::is_same<common_type_impl_t<const std::chrono::milliseconds, 
            std::chrono::microseconds &&>, 
       std::chrono::microseconds>::value, ""); 

其通過。現在,將volatile扔到那裏,就像@Vincent指出的那樣,它會破裂,這進一步證明專業化沒有被使用。

所以我的結論是,兩個人會發生:

  • 標準的實施將改變這樣的,即使不存在CV-和參考 - 預選賽特會習慣的,在這種情況下你可以折騰CommonType<>包裝,無論如何都是微不足道的,你只定義了一個專業化。
  • 標準實現不會改變,但您仍然只定義了一個專業化,而您使用CommonType<>來代替。
+0

我想'chrono'的作品是因爲'持續時間'的隱式轉換,並不是因爲'common_type'的特殊化。當然,這對OP來說不起作用(結果類型可能是第三種類型)。 'volatile'不起作用,因爲轉換需要一個'const&',而不是'const volatile&'。 – dyp