【问题标题】:How to declare a variadic template function *instance* as a friend如何将可变参数模板函数 *instance* 声明为朋友
【发布时间】:2018-12-02 07:28:17
【问题描述】:

我的问题扩展这个:How to declare a variadic template function as a friend?

我知道如何使模板可变参数函数成为类的朋友 所有模板参数的实例化。 但我不知道如何仅通过一些明确列出的实例来交朋友。

例子:

template <typename T, typename... Args>
T create(Args&&... args)
{
    T t(forward<Args>(args)...);
    // ...
    return t;
}

class A {
public:
    /* -- This would allow to use all versions of constructors ..
    template <typename T, typename... Args>
    friend T create(Args&&... args);
    */
    // How to make these work?
    friend A create<A>(int);
    friend A create<A>(int, int);
    // .. is replacing T by A necessary at all?
protected:
    A() = default;  // < this should 'stay protected'!
    A(int) { }
    A(int, int) { }
};

我们的目标是只与create的某些版本交朋友, 这意味着我只想要一些版本的构造函数 可以公开访问,但其他人不能访问。

我也希望朋友creates 只坚持A 返回类型(和构造函数),但也许根本没有必要,因为朋友没有继承,并且除A 之外的其他构造函数可能不会受到内部影响A?

该解决方案可能需要最高 C++14 的功能,但最好不要来自较新的标准。

编辑

如果我只需要像int 这样的基本类型,那么@ritesh 解决方案将完全令人满意,因为复制元素与移动一样有效。 但是,我的用例实际上是不同的,需要避免不必要地复制更复杂的对象,并且构造函数可以具有按值传递的参数,无论是作为副本还是移入(我不记得这个成语是如何调用的..请参见下面的示例)。

我已经完成了这个解决方案,它使用(并检查)@Jarod42 提出的前向引用。我使用了一个辅助结构 E 来演示复制和移动它的对象。

template <typename T, typename... Args>
T create(Args&&... args)
/** ++ This would have copy E even if it is not necessary ..
T create(Args... args)
*/
{
    T t(forward<Args>(args)...);
    // ...
    return t;
}

struct E {
    E() { cout << "E()" << endl; }
    E(const E&) { cout << "E(const E&)" << endl; }
    E& operator =(const E&) { cout << "= const E&" << endl; return *this; }
    E(E&&) { cout << "E(E&&)" << endl; }
    E& operator =(E&&) { cout << "= E&&" << endl; return *this; }
};

class A {
public:
    /** -- This would allow to use all versions of constructors ..
    template <typename T, typename... Args>
    friend T create(Args&&... args);
    */

    friend A create<A>(const E&);
    friend A create<A>(E&);
    friend A create<A>(E&&);
    friend A create<A>(const E&, const E&);
    friend A create<A>(const E&, E&);
    friend A create<A>(const E&, E&&);
    friend A create<A>(E&, const E&);
    friend A create<A>(E&, E&);
    friend A create<A>(E&, E&&);
    friend A create<A>(E&&, const E&);
    friend A create<A>(E&&, E&);
    friend A create<A>(E&&, E&&);

    /** ++ This would have copy E even if it is not necessary ..
    friend A create<A>(E);
    friend A create<A>(E, E);
    */
protected:
    A() = default;  //< this should 'stay protected'!
    A(E) { }  //< either copy or move E
    A(const E&, E) { }  //< must not copy the first argument
};

幸运的是,仅将具有引用参数类型(const E&amp;E&amp;E&amp;&amp;)的变体声明为友元就足够了,而不是 E:这就是 forward 将处理的问题。

测试:

E e;
const E ce;

cout << endl << "(E) <- (e) ..." << endl;
create<A>(e);
cout << endl << "(E) <- (ce) ..." << endl;
create<A>(ce);
cout << endl << "(E) <- (E()) ..." << endl;
create<A>(E());

cout << endl << "(const E&, E) <- (e, e) ..." << endl;
create<A>(e, e);
cout << endl << "(const E&, E) <- (ce, ce) ..." << endl;
create<A>(ce, ce);
cout << endl << "(const E&, E) <- (ce, e) ..." << endl;
create<A>(ce, e);
cout << endl << "(const E&, E) <- (ce, E()) ..." << endl;
create<A>(ce, E());
cout << endl << "(const E&, E) <- (move(e), E()) ..." << endl;
create<A>(move(e), E());

.. 将产生:

E()
E()

(E) <- (e) ...
E(const E&)

(E) <- (ce) ...
E(const E&)

(E) <- (E()) ...
E()
E(E&&)

(const E&, E) <- (e, e) ...
E(const E&)

(const E&, E) <- (ce, ce) ...
E(const E&)

(const E&, E) <- (ce, e) ...
E(const E&)

(const E&, E) <- (ce, E()) ...
E()
E(E&&)

(const E&, E) <- (move(e), E()) ...
E()
E(E&&)

我相信现在这个解决方案有效,只是有点不舒服,因为有必要声明所有引用组合(包括E&amp;,否则传递e 将不起作用)。 如果有人知道如何缩短它(可能是通过使用模板,而不是宏,我尽量避免使用它们),请告诉我。

【问题讨论】:

    标签: c++ c++14


    【解决方案1】:

    根据您的签名,应该是这样的:

    friend A SOS::create<A>(int&&);
    friend A SOS::create<A>(int&&, int&&);
    
    friend A SOS::create<A>(int&);
    friend A SOS::create<A>(int&, int&);
    
    friend A SOS::create<A>(const int&);
    friend A SOS::create<A>(const int&, const int&);
    
    // ... mixing int&&, const int&    handles volatile
    

    Demo

    【讨论】:

    • 一个疑问:为什么是int &amp;&amp; 而不是int const &amp;
    • @max66:取决于 OP 想要成为朋友的哪个版本,(可能确实有几个)。
    • 是的...也许是两个 int 版本的组合...所以 9 个原型...但是使用 int const &amp; 不能全部捕获?或者当我们传递一个int 变量(所以int &amp;,而不是const)时,我们失去了友谊?
    • 如果你想完整,不要忘记所有可能的volatiles。
    • @max66: SOS::create&lt;A&gt;(int&amp;); 确实与SOS::create&lt;A&gt;(const int&amp;); 不同,所以不要分享友谊。
    【解决方案2】:

    此代码将起作用。

    template <typename T, typename... Args>
    T create(Args... args)
    {
        return T(std::forward<Args>(args)...);
    
    }
    
    class A {
    public:
        /*This will work*/
        friend A create<A>(int);
        friend A create<A>(int, int);
        void foo() { std::cout << "in foo" << std::endl; };
        // .. is replacing T by A necessary at all? 
    
    protected:
        A() = default;  // < this should 'stay protected'!
        A(int) { };
        A(int, int) { };
    
    };
    

    回答你的问题

    // .. 是否有必要将 T 替换为 A?

    是的。因为不将其替换为专用类型会将其称为部分专业化。友元声明不允许引用部分特化。只允许完全专业化。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-01-28
      • 2011-05-01
      • 2011-03-18
      相关资源
      最近更新 更多