簡體   English   中英

為什么是模板 <typename…> 不被識別為通過模板實例化 <template<typename> 類型名&gt;?

[英]Why is a template<typename…> not recognized as instantiatable through template<template<typename> typename>?

我試圖任意“綁定”模板參數,但遇到了一個優雅的問題。

為了直接解決潛在的問題,gcc 6.2有以下問題,但從邏輯上講,我認為它沒有問題......

template<template<typename, typename> P, typename A, typename B>
struct foo {
    static constexpr bool value = P<A, B>::value;
};

template<typename...Ts>
struct bar {
    static constexpr bool value = true;
};

... foo給定的barfoo<bar, void, void>應該導致bar<void, void> (這是有效的)的實例化,誰的value成員為true ,因此foo<bar, void, void>::value同樣true 實際上 ,這應該 (在我看來)導致實例化的結構在概念上類似於......

struct bar<void, void> {
    static constexpr bool value = true;
};

struct foo<bar, void, void> {
    static constexpr bool value = bar<void, void>::value; //which is true
};

你可以在這里看到這個概念(或者更確切地說是錯誤) https://godbolt.org/g/lT9umg

回到現在開始,首先我嘗試了以下......

template<typename...>
struct type_list { };

template<template<typename...> typename Tmpl, typename...Ts>
struct bind_template {
    template<typename...Us>
    using type = Tmpl<Ts..., Us...>;
};

template<template<typename> typename Predicate, typename...Ts>
struct has_matching_type {
    private:
        template<template<typename> typename, typename, typename=void>
        struct helper: std::false_type { };
        template<template<typename> typename P, typename U, typename...Us>
        struct helper<P, type_list<U, Us...>, typename std::enable_if<P<U>::value>::type>: std::true_type { };
        template<template<typename> typename P, typename U, typename...Us>
        struct helper<P, type_list<U, Us...>, typename std::enable_if<!P<U>::value>::type>: helper<P, type_list<Us...>> { };
    public:
        static constexpr bool value = helper<Predicate, type_list<Ts...>>::value;
};

template<typename T, typename...Ts>
using has_type = has_matching_type<bind_template<std::is_same, T>::template type, Ts...>;

后來我可能嘗試通過has_type<T, Ts...>實例化has_type<T, Ts...>例如......

cout << has_type<long, int, bool, long, float>::value << endl;

但是,正如我指出gcc 6.2.0抱怨因為它似乎沒有認識到,一旦完成解析,模板實例化在實用上是等效的。

只需知道模板參數的數量並專門針對該確切數字就可以解決問題。 如果我專門研究bound_template ,請記住std::is_same<LHS, RHS>

template<template<typename, typename> typename Tmpl, typename T>
struct bind_template<Tmpl, T> {
    template<typename U>
    using type = Tmpl<T, U>;
};

...我們突然編譯並評估編譯時沒問題,因為gcc將bind_template<std::is_same, long>::type視為完全采用一個類型參數。

顯然,將這​​個概念抽象出來以允許任何模板參數,例如積分常數而不僅僅是類型,無論編譯器如何都是一個基本問題。 僅僅關注一分鍾的類型,我的問題是多方面的:

  1. 我是否在概念上遺漏了一些內容,編譯器實際上正在做的事情對我來說應該是顯而易見的?
  2. 如果沒有,這是否違反了C ++ 11標准,不是由標准指導,還是編譯器依賴?
  3. 無論我前兩個問題的答案是什么,我是否有一些優雅的方式可以解決這個問題?

在功能上,真正的問題(特別是如果這是C ++ 11中不可避免的問題)是......

是否有一些優雅的方式我可以抽象地綁定模板而不必專門針對每個案例(這里最簡單的是n種類型)?

只要能夠得到問題1或3的答案就會很棒。 問題3是最重要的,因為在一天結束時,重要的是有效的。

顯然,我可以專攻(如上所示)。 一個很大的問題是,即使以下似乎也不起作用(至少根據這個在線編譯器 )...

template<template<typename...> class Tmpl, typename... Ts>
struct bind_helper {
    template<typename... Us>
    struct type: Tmpl<Ts..., Us...> { };
    template<typename A>
    struct type<A>: Tmpl<Ts..., A> { };
    template<typename A, typename B>
    struct type<A, B>: Tmpl<Ts..., A, B> { };
    template<typename A, typename B, typename C>
    struct type<A, B, C>: Tmpl<Ts..., A, B, C> { };
};

這意味着,我不僅需要生成一堆參數,而且還必須通過完整的bind_template來匹配外部參數。 這很快成為(實際上)二項式問題。

進一步擴展這個概念(但仍然保持類型),我計划下一步實現“占位符”,就像std::bind使用占位符一樣(因為我只是剝離並重新加入索引列表)。 顯然,如果沒有更抽象的方法,這就太麻煩了。

這在C ++ 17中得到修復。 具體而言,從14.3.3“模板模板參數”,第3段:

 template<class T> class A { /* ... */ }; template<class T, class U = T> class B { /* ... */ }; template<class ... Types> class C { /* ... */ }; template<auto n> class D { /* ... */ }; template<template<class> class P> class X { /* ... */ }; template<template<class ...> class Q> class Y { /* ... */ }; template<template<int> class R> class Z { /* ... */ }; X<A> xa; // OK X<B> xb; // OK X<C> xc; // OK Y<A> ya; // OK Y<B> yb; // OK Y<C> yc; // OK Z<D> zd; // OK 

這里的相關示例是X<C> 這適用於g ++ 7,標志為-std=c++1z

C ++ 14指定上面的示例格式錯誤:

 template<class T> class A { /∗ ... ∗/ }; template<class T, class U = T> class B { /∗ ... ∗/ }; template <class ... Types> class C { /∗ ... ∗/ }; template<template<class> class P> class X { /∗ ... ∗/ }; template<template<class ...> class Q> class Y { /∗ ... ∗/ }; X<A> xa; // OK X<B> xb; // ill-formed: default arguments for the parameters of a template argument are ignored X<C> xc; // ill-formed: a template parameter pack does not match a template parameter 

這一變化發生在2016年末的DR論文中:匹配模板模板 - 參數排除了兼容的模板 此更改自11月起應用於此提交 委員會自1999年或之前知道這個問題。

在C ++ 17之前,由於jbapple的答案中提到的不幸的語言缺陷,這段代碼是按原樣形成的

符合C ++ 11標准的解決方案是從使用各地的元函數切換到在任何地方使用元函數類。 Boost.MPL定義中的元函數類將是一個具有名為apply的模板別名的類型。 我們可以這樣調用:

template <class MFC, class... Ts>
using apply_t = typename MFC::template apply<Ts...>;

我們可以通過以下方式將模板提升為元函數類:

template <template <typename...> class Z>
struct quote {
    template <class... Args>
    using apply = Z<Args...>;
};

然后讓我們重寫bind_template來獲取元函數類而不是模板:

template <class MFC, class... Ts>
struct bind_template {
    template <class... Us>
    using apply = apply_t<MFC, Ts..., Us...>;
};

然后重寫has_matching_type以獲取元函數類而不是模板:

template<class Predicate, class... Ts>
struct has_matching_type {
private:
    template<class>
    struct helper: std::false_type { };

    template<typename U, typename...Us>
    struct helper<type_list<U, Us...>>
        : std::conditional<
            apply_t<Predicate, U>::value,
            std::true_type,
            helper<type_list<Us...>>
            >::type
     { };

public:
    static constexpr bool value = helper<type_list<Ts...>>::value;
};

template<class T, class... Ts>
using has_type = has_matching_type<bind_template<quote<std::is_same>, T>, Ts...>;

現在你的初始has_type<long, int, bool, long, float>::valuetrue ,即使在C ++ 11中也是true

暫無
暫無

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

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