代码之家  ›  专栏  ›  技术社区  ›  asynts

模板:只允许出现在模板参数[duplicate]中的类型

  •  1
  • asynts  · 技术社区  · 7 年前

    假设我有一个可变基类模板:

    template <typename ... Types>
    class Base
    {
    public:
        // The member foo() can only be called when its template 
        // parameter is contained within the Types ... pack.
    
        template <typename T>
        typename std::enable_if<Contains<T, Types ...>::value>::type
        foo() {
            std::cout << "Base::foo()\n";
        }
    };
    

    这个 foo() 成员只能在其模板参数与的至少一个参数匹配时调用 Base (b)执行 Contains 在这篇文章的底部列出):

    Base<int, char>().foo<int>(); // fine
    Base<int, char>().foo<void>(); // error
    

    现在我使用 类型:

    struct Derived: public Base<int, char>,
                    public Base<double, void>
    {};
    

    我希望打电话时。

    Derived().foo<int>();
    

    编译器会找出要使用的基类,因为它是从不包含 int

    我的问题有两个:

    1. 为什么编译器不能解决这种模糊性(一般兴趣)?
    2. Derived().Base<int, char>::foo<int>(); 编辑: GuyGreer告诉我,当我添加两个using声明时,调用被消除了歧义。但是,由于我提供了基类供用户从中继承,所以这不是一个理想的解决方案。如果可能的话,我不希望我的用户必须将这些声明(对于大型类型列表来说,这些声明可能非常冗长和重复)添加到他们的派生类中。

    包含 :

    template <typename T, typename ... Pack>
    struct Contains;
    
    template <typename T>
    struct Contains<T>: public std::false_type
    {};
    
    template <typename T, typename ... Pack>
    struct Contains<T, T, Pack ...>: public std::true_type
    {};
    
    template <typename T, typename U, typename ... Pack>
    struct Contains<T, U, Pack ...>: public Contains<T, Pack...>
    {};
    
    0 回复  |  直到 5 年前
        1
  •  16
  •   Barry    7 年前

    下面是一个简单的例子:

    template <typename T>
    class Base2 {
    public:
        void foo(T ) { }
    };
    
    struct Derived: public Base2<int>,
                    public Base2<double>
    {};
    
    int main()
    {
        Derived().foo(0); // error
    }
    

    否则(即,C不包含f的声明或得到的声明集为空),S(f,C)为空 并将每个这样的查找集S(f,Bi)依次合并为S(f,C)。
    [..]

    因为我们的初始声明集是空的( Derived 没有方法),我们必须从所有的基中合并-但是我们的基有不同的集合,所以合并失败。但是,该规则仅在 C 派生 )是空的。所以为了避免它,我们将它设为非空:

    struct Derived: public Base2<int>,
                    public Base2<double>
    {
        using Base2<int>::foo;
        using Base2<double>::foo;
    };
    

    这是因为 using

    在声明集中, 使用声明 被集合替换 未被派生类(7.3.3)的成员隐藏或重写的指定成员,

    对于成员之间是否存在分歧没有任何评论-我们实际上只是提供 打开两个重载 foo ,绕过成员名称查找合并规则。

    现在, Derived().foo(0) 毫不含糊地呼叫 Base2<int>::foo(int ) .


    使用

    template <typename... Bases>
    struct BaseCollector;
    
    template <typename Base>
    struct BaseCollector<Base> : Base
    {
        using Base::foo;
    };
    
    template <typename Base, typename... Bases>
    struct BaseCollector<Base, Bases...> : Base, BaseCollector<Bases...>
    {
        using Base::foo;
        using BaseCollector<Bases...>::foo;
    };
    
    struct Derived : BaseCollector<Base2<int>, Base2<std::string>>
    { };
    
    int main() {
        Derived().foo(0); // OK
        Derived().foo(std::string("Hello")); // OK
    }
    

    在C++ 17中,你可以 pack expand using declarations 也就是说,这可以简化为:

    template <typename... Bases>
    struct BaseCollector : Bases...
    {
        using Bases::foo...;
    };
    

    这不仅缩短了编写时间,而且提高了编译效率。双赢。

        2
  •  3
  •   SirGuy    10 年前

    using Base<int, char>::foo; using Base<double, void>::foo; Derived 它现在编译得很好。

    测试 clang-3.4 gcc-4.9