簡體   English   中英

匹配任何類型參數的 C++ 可變參數模板模板參數

[英]C++ variadic template template argument that matches any kind of parameters

我想知道是否有可能編寫一個模板函數,它可以將任何其他任意模板作為參數並正確匹配模板名稱(即不僅僅是生成的類)。 我所知道的工作是這樣的:

template<template<typename ...> class TemplateT, typename... TemplateP>
void f(const TemplateT<TemplateP...>& param);

例如,這將匹配f(std::vector<int>())f(std::list<int>())但不適用於f(std::array<int, 3>()) ,因為第二個參數是size_t並且沒有類型。

現在我想人們可以做一些瘋狂的事情,比如:

template<template<typename ...> class TemplateT, size... Sizes, typename... TemplateP>
void f(const TemplateT<Sizes..., TemplateP...>& param);

希望編譯器能夠正確地將TemplateP省略號或Sizes省略號導出為空。 但它不僅丑陋,而且它仍然只適用於采用 types 或size_t參數的模板。 它仍然不會匹配任意模板,例如帶有bool參數。

重載方法也是如此:

template<template<typename ...> class TemplateT, typename... TemplateP>
void f(const TemplateT<TemplateP...>& param);

template<template<typename ...> class TemplateT, size... Sizes>
void f(const TemplateT<Sizes...>& param);

此外,如果我們想混合使用size_ttypenames ,這種方法將不起作用。 因此,匹配任何內容所需的內容將是這樣的,其中對省略號中允許的內容完全沒有限制:

template<template<...> class TemplateT, ... Anything>
void f(const TemplateT<Anything...>& param);

該語法不起作用,但也許還有其他語法來定義這樣的東西?

這主要是我想知道語言中有什么可能,認為它實際上可能有用,如果您有不同的模板,其中第一個參數總是固定的,並且您想根據返回類型更改它並保留其他所有內容. 像這樣的東西:

template<
    template<typename ValueT, ...> class TemplateT,
    ... Anything,
    typename ValueT,
    typename ResultT = decltype(some_operation_on_value_t(std::declval<ValueT>())>
TemplateT<ResultT, Anything...> f(const TemplateT<ValueT, Anything...>& in);

那么,有什么方法可以使用模式匹配以完全通用的方式完成這項工作?

這不僅僅是一個思想實驗,因為我遇到的用例是創建對容器進行操作的純函數原語,並將隱式構造不可變的結果容器。 如果結果容器有不同的數據類型,我們需要知道容器操作的類型,所以對任何容器的唯一要求是模板的第一個參數需要是輸入類型,以便可以用不同的類型替換結果中的輸出類型,但代碼應該忽略其后的任何模板參數,並且不應該關心它是類型還是值。

您有趣的構造有兩個帶有可變參數模板的級別。

  • 函數模板的外部可變參數模板參數列表TemplateP & Sizes
  • 一個內部參數包作為你的模板模板參數TemplateT模板參數,一個類模板

首先,讓我們看看內部TemplateT類:為什么省略號運算符不能匹配TemplateT< int, 2 > 好吧,該標准在 §14.5.3 中將可變參數模板定義為

template<class ... Types> struct Tuple { };
template<T ...Values> struct Tuple2 { };

其中在第一種情況下的模板參數組可以匹配的類型和在所述第二版本唯一值類型的T 特別是,

Tuple < 0 >    error;  // error, 0 is not a type!
Tuple < T, 0 > error2; // T is a type, but zero is not!
Tuple2< T >    error3; // error, T is not a value
Tuple2< T, 0 > error4; // error, T is not a value

都是畸形的。 此外,不可能退回到類似的東西

template<class ... Types, size_t ...Sizes> struct Tuple { };

因為標准在 §14.1.11 中指出:

如果主類模板或別名模板的模板參數是模板參數包,則它應是最后一個模板參數。 一個函數模板的模板參數包后面不能跟另一個模板參數,除非該模板參數可以從函數模板的參數類型列表中推導出來或者有一個默認參數(14.8.2)。

換句話說,對於類模板,定義中只能出現一個可變參數包。 因此,上述(雙)可變參數類定義格式錯誤。 因為內部類總是需要這樣的組合,所以不可能寫出你想象的那么籠統的東西。


能救出什么? 對於外部函數模板,可以將一些分片放在一起,但您不會喜歡它。 只要可以從第一個參數包推導出第二個參數包,就可能出現兩個參數包(在函數模板中)。 因此,一個函數,如

template < typename... Args, size_t... N > void g(const std::array< Args, N > &...arr);
g(std::array< double, 3 >(), std::array< int, 5>());

是允許的,因為可以推導出整數值。 當然,這必須針對每種容器類型進行專門化,並且與您的想象相去甚遠。

您必須有一個重新綁定容器類型的元函數。 因為你不能只替換第一個模板參數:

vector<int, allocator<int> > input;
vector<double, allocator<int> > just_replaced;
vector<double, allocator<double> > properly_rebound;

因此,只需為已知的容器集編寫這樣一個元函數。

template<class Container, class NewValue> class rebinder;

// example for vectors with standard allocator
template<class V, class T> class rebinder< std::vector<V>, T > {
public:
  typedef std::vector<T> type;
};
// example for lists with arbitrary allocator
template<class V, class A, class T> class rebinder< std::list<V,A>, T > {
  typedef typename A::template rebind<T>::other AT; // rebind the allocator
public:
  typedef std::list<T,AT> type; // rebind the list
};
// example for arrays
template<class V, size_t N> class rebinder< std::array<V,N>, T > {
public:
  typedef std::array<T,N> type;
};

不同容器的重新綁定規則可能有所不同。

此外,您可能需要一個從任意容器中提取值類型的元函數,而不僅僅是符合標准的( typedef *unspecified* value_type

template<class Container> class get_value_type {
public:
  typedef typename Container::value_type type; // common implementation
};
template<class X> class get_value_type< YourTrickyContainer<X> > {
  ......
public:
  typedef YZ type;
};

如果我們有這樣的東西會很棒,因為它可以讓我們輕而易舉地編寫is_same_template特征。
在那之前,我們一直專注於專業。

暫無
暫無

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

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