2017-06-12 83 views
5

說我有一些模板類型...傳遞指定的模板類型的模板參數

template <typename T> struct Foo { 
    Foo(T t) {} 
}; 

有沒有辦法到指定的Foo類型傳遞給函數,這樣的功能是T的直接知名度?

理想我想能寫這樣的事情...

Foo<int> foo = create<Foo<int>>(); 

我已經能來最接近的是

template < 
    template <typename> typename TT, 
    typename T, 
    std::enable_if_t<std::is_same<TT<T>, Foo<T>>::value, int> = 0 
> 
Foo<T> create() { 
    return Foo<T>(T()); 
} 

這將然後像

使用
Foo<int> foo = create<Foo, int>(); 

感謝您的任何幫助。

+0

所以,實質上,你需要一個'create'函數,它只需要一個'Foo '作爲模板類型並返回一個'Foo '? – NathanOliver

+2

不能簡單地是'美孚富= createFoo ();' - 作爲構造類型永遠是'美孚'呢? – axalis

+0

@axalis是的,我可以做'createFoo ()',而不是'創建>()',但是我正在尋找我可以概括爲其他類型的解決方案,例如'創建>' – Daskie

回答

5

此表單模板模板參數只允許在C++ 17:

template < //   v---------- typename here not allowed 
    template <typename> typename TT, 
    typename T, 
    std::enable_if_t<std::is_same<TT<T>, Foo<T>>::value, int> = 0 
> 
Foo<T> create() { 
    return Foo<T>(T()); 
} 

您必須更換typename指出的class

template < //   v---------- class allowed 
    template <typename> class TT, 
    typename T, 
    std::enable_if_t<std::is_same<TT<T>, Foo<T>>::value, int> = 0 
> 
Foo<T> create() { 
    return Foo<T>(T()); 
} 

在C++ 17,這兩個編譯和等同。


爲了讓你的語法Foo<int> foo = create<Foo<int>>();工作,你只需要做到這一點:

template <typename T> 
T create() { 
    return T{}; 
} 

如果要限制可以發送什麼類型的,你必須創建一個類型特點:

// default case has no typedef 
template<typename> 
struct first_param {}; 

// when a template is sent, define the typedef `type` to be equal to T 
template<template<typename> class TT, typename T> 
struct first_param<TT<T>> { 
    using type = T; 
}; 

// template alias to omit `typename` everywhere we want to use the trait. 
template<typename T> 
using first_param_t = typename first_param<T>::type; 

然後,用你的特質:

template < 
    typename T, 
    void_t<first_param_t<T>>* = nullptr 
> //  ^---- if the typedef is not defined, it's a subtitution error. 
T create() { 
    return T(first_param_t<T>{}); 
} 

您可以實現void_t這樣的:

template<typename...> 
using void_t = void; 

Live at Coliru

+0

因爲它似乎編譯爲OP,我不認爲這個問題。這個問題似乎是如何「修復」模板,以允許一個人寫'Foo foo =創建>();'而不是'Foo foo =創建();' – NathanOliver

+0

@NathanOliver固定,謝謝指向出。 –

+1

太棒了。無論OP需要什麼,現在都應該工作。 +1 – NathanOliver

4

爲什麼不能簡單地用一個標籤調度,如:

template <class> 
struct tag { }; 

template <class T> 
Foo<T> create(tag<Foo<T>>) { 
    return Foo<T>(T()); 
} 

//... 

Foo<int> foo = create(tag<Foo<int>>{}); 
+0

這絕對有效,但我正在尋找一個沒有功能參數的解決方案。 – Daskie

+0

@Daskie重新考慮你的架構 - 該函數調用將被編譯器優化像它不會有任何參數反正... –

2

在C++ 11

Demo

要點是要有一個名爲create的入口函數,它可以實例化一個create_helper結構來創建正確的類型。

我們可以使用模板專門化來創建我們的結構,以便強制傳遞模板類。

全碼:

template<class T> 
struct create_helper 
{ 
    static_assert(sizeof(T) == 0, "Need to pass templated type to create"); 
}; 

template <class T, template<class> class TT> 
struct create_helper<TT<T>> 
{ 
    static TT<T> apply() 
    { 
     return {T{}}; 
    } 
}; 

template<class T> 
auto create() -> decltype(create_helper<T>::apply()) 
{ 
    return create_helper<T>::apply(); 
} 

和測試:

template<class T> 
struct Foo 
{ 
    Foo(T t){std::cout << "Constructed Foo with value " << t << std::endl;} 
}; 
int main() 
{ 
    Foo<int> foo = create<Foo<int>>(); 
} 

輸出:

Constructed Foo with value 0 
+0

注意,基本模板'create_helper'不需要用'static_assert'死亡。如果你願意,你可以添加一個'apply'功能就像我們與專業化確實創造T'的'默認實例。 [演示](https://wandbox.org/permlink/bAcedPFMk3I1svmI) – AndyG

3

一個簡單的方法就是在Foo直接添加子類型信息:

template <typename T> struct Foo { 
    using type = T; 
    Foo(T t) {} 
}; 

然後

template <typename FooT> 
FooT create() { 
    return FooT(typename FooT::type{}); 
} 

,如果您願意,您可以添加SFINAE:

template <typename FooT> 
auto create() 
-> decltype(FooT(typename FooT::type{})) 
{ 
    return FooT(typename FooT::type{}); 
} 

如果你想真正制約的功能Foo獨佔,必須在其上創建特徵和SFINAE。

+0

特質和SFINAE似乎是要走的路 – Daskie

相關問題