使用类模板参数推导来创建静态接口

时间:2019-01-31 20:22:53

标签: c++ c++17

我想创造一些宏来创建模板参数传递,存储,等我使用类模板参数推导静态接口,但我打墙。

#include <iostream>

template <typename Type>
struct Person
{
    Type &object;

    Person(Type &object) : object(object) {}

    void walk(unsigned steps)
    {
        object.walk(steps);
    }

    void talk(const std::string &words)
    {
        object.talk(words);
    }
};

struct MySelf
{
    void walk(unsigned steps)
    {
        std::cout << "walking: " << steps << std::endl;
    }

    void talk(const std::string &words) const
    {
        std::cout << "talking: " << words << std::endl;
    }
};

template <typename Type>
void testNConst(Person<Type> object)
{
    object.walk(50);
    object.talk("testing");
}

template <typename Type>
void testConst(Person<const Type> object)
{
    object.talk("testing");
}

int main()
{
    MySelf myself;

    testNConst(Person{myself}); // compiles

    testNConst(myself);         // does not compile
    testConst(myself);          // does not compile

    return 0;
}

输出:

../../../../src/py.com.personal/other/hanaTest/main.cpp:53:5: error: no matching function for call to 'testNConst'
    testNConst(myself);         // does not compile
    ^~~~~~~~~~
../../../../src/py.com.personal/other/hanaTest/main.cpp:35:6: note: candidate template ignored: could not match 'Person<type-parameter-0-0>' against 'MySelf'
void testNConst(Person<Type> object)
     ^
../../../../src/py.com.personal/other/hanaTest/main.cpp:54:5: error: no matching function for call to 'testConst'
    testConst(myself);          // does not compile
    ^~~~~~~~~
../../../../src/py.com.personal/other/hanaTest/main.cpp:42:6: note: candidate template ignored: could not match 'Person<const type-parameter-0-0>' against 'MySelf'
void testConst(Person<const Type> object)
     ^
2 errors generated.

有什么想法吗?

2 个答案:

答案 0 :(得分:2)

类模板自变量推导适用于创建对象(变量声明等)。

仅适用于函数参数或函数返回类型。您无法调用testNConst(myself),因为对于某些myself来说Person<T>不是T-适用常规函数推导规则。


简而言之:

template <typename T> struct X { X(T ); }; 

X x = 42;                 // ok, ctad here

template <typename T>
void foo(X<T> );
foo(42);                  // error, ctad doesn't apply here

X bar() { return 42; }    // error, ctad doesn't apply here either

答案 1 :(得分:0)

另一种方法可能是使用curiously recurring template pattern (CRTP),它从接口继承,该接口将类型本身作为模板参数,请记住,您可以使用static_cast进行下转换,并且使用接口时,重载解析没有问题作为参数。您必须知道您不能使用Person类型的对象(如果未子类化)。因此,您必须通过引用将对象传递给函数(比复制对象要快)。 接口不是驻留在Person内部的Type类型的对象,而是驻留在Type本身内部。 (接口没有任何成员,当从空结构继承时,没有额外的内存开销,MySelf的大小与没有继承的情况相同)。使用这种方法,切勿在参数列表中没有Person<Type>const&&的情况下使用&&

#include <iostream>

template <typename Type>
struct Person
{
    /// this returns the subclass-object
    Type &object() { return static_cast<Type&>(*this); }
    Type const &object() const { return static_cast<Type const&>(*this); }

    void walk(unsigned steps)
    {
        object().walk(steps);
    }

    void talk(const std::string &words) const /// const was eventually missing
    {
        object().talk(words);
    }

protected:
    ~Person() = default; /// this disallows the user to construct an instance of this class that is not used as a base object
};

struct MySelf : Person<MySelf>
{
    void walk(unsigned steps)
    {
        std::cout << "walking: " << steps << std::endl;
    }

    void talk(const std::string &words) const
    {
        std::cout << "talking: " << words << std::endl;
    }
};

template <typename Type>
void testNConst(Person<Type>& object) /// works fine with instances of MySelf and Person<MySelf>
{
    object.walk(50);
    object.talk("testing");
}

template <typename Type>
void testConst(Person<Type> const& object)
{
    object.talk("testing");
}

int main()
{
    MySelf myself;

    testNConst(myself);         // compiles
    testConst(myself);          // compiles

    return 0;
}

其他一些提示

  • 如果要更改对象,请始终按引用传递对象
  • 如果您不想更改对象,则始终通过const引用传递对象

编辑

  • 受保护的析构函数避免了在没有派生类的情况下实例化该类,这防止了程序员以其他方式调用未定义的行为(static_cast<Type&>是关键点)。