【问题标题】:Static introspection of method argument type in C++ [duplicate]C ++中方法参数类型的静态自省[重复]
【发布时间】:2019-01-21 08:16:17
【问题描述】:

我想创建一个模板类,它包含另一个类的实例,并使用正确的参数类型转发其 foo 方法之一。是否有一种巧妙的元编程方式对内部方法进行“完美转发”?

template <typename Inner>
class Outer {
private:
  Inner inner;

public:

  // To-do: replicate foo method of Inner with identical signature,
  // how to pick correct T?
  void foo(T arg) { inner.foo(arg); }

};

我可以看到两个经典的解决方案,但有没有更好的现代解决方案与元编程?

  • 具有部分受保护接口的继承:Outer 可以从 Inner 公开继承。但是 Inner 也有只能由 Outer 而不是用户调用的方法。可以是protected,好的,但它也紧密耦合了Outer 和所有类型的Inner 类的实现。 Outer的公共接口可以通过Inner中的公共方法任意扩展,这是不可取的。
  • 使 foo 成为模板函数 template &lt;typename T&gt; void foo(T&amp;&amp; arg) { inner.foo(std::forward&lt;T&gt;(arg)); }。这是参数的完美转发,但是如果用户使用错误的参数调用foo,则错误报告Inner::foo 而不是Outer::foo。这打破了Outer的公共接口提供的封装。

【问题讨论】:

标签: c++ inheritance metaprogramming introspection


【解决方案1】:

也许是这样的:

template <typename Inner>
class Outer : private Inner {
public:
  using Inner::foo;
};

Demo

【讨论】:

  • 我认为OP的第一点和这个答案是一样的。
  • @appleapple OP 担心意外暴露foo 以外的方法关闭Outer。这种技术并没有那么暴露。
  • 可能要注意,如果foo 被重载,它将暴露所有重载。我不确定 OP 是否关心这一点,但有人可能会。
  • 酷,是的,这是一个可以接受的解决方案。而且非常直截了当。我会再等一会儿,但我想这将是公认的答案,即使它不使用元编程:)
【解决方案2】:

这是给出几乎完美错误消息的一种方式:

#include <string>

// a type which yields the type we gave it
template<class T> struct passer 
{ 
    using type = T; 
};

// an easy-to-use alias
template<class T> using pass_t = typename passer<T>::type;

// example
template <typename Inner>
class Outer {
private:
  Inner inner;

public:

  // To-do: replicate foo method of Inner with identical signature,
  // how to pick correct T?
  // Ans: with a pass_t
    template<class T>
    auto foo(T&& arg) 
    -> pass_t<decltype(this->inner.foo(std::forward<T>(arg)))>
    {
       return inner.foo(std::forward<T>(arg)); 
    }

};


struct Bar
{
    void foo(std::string const& thing);
};

struct Baz
{
    int foo(int thing) { return thing * 2; };
};

int main()
{

    auto o = Outer<Bar>();
    o.foo(std::string("hi"));
    o.foo("hi");

    int i = 1;

    /* - uncomment for error
    o.foo(i);

    note the nice error message on gcc:
        <source>:41:7: error: no matching member function for call to 'foo'
        <source>:19:10: note: candidate template ignored: substitution failure [with T = int]: reference to type 'const std::string' (aka 'const basic_string<char>') could not bind to an lvalue of type 'int'
    */

// same here:
//    o.foo(1);

// but this is fine
    auto o2 = Outer<Baz>();
    auto x = o2.foo(2);

    // and this is not
    // note: candidate template ignored: substitution failure [with T = char const (&)[6]]: cannot initialize a parameter of type 'int' with an lvalue of type 'char const[6]'
//    auto y = o2.foo("dfghj");

}

链接:https://godbolt.org/g/UvsrbP

【讨论】:

  • 这是一个非常有见地的答案,谢谢。我更喜欢using Inner::foo 的答案,即使它不使用元编程,但它解决了我在公共继承方面遇到的问题。
  • @olq_plo 哦,我以为你想要一个用于封装的转发解决方案。没关系,我会把它留在这里,以防它对某人有用。
  • 事实证明 Boost.CallableTraits 可以做我想做的事,更具体地说是args_t 元函数。 boost.org/doc/libs/1_68_0/libs/callable_traits/doc/html/…
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-08-06
  • 1970-01-01
  • 2019-09-06
  • 1970-01-01
  • 1970-01-01
  • 2011-01-17
  • 1970-01-01
相关资源
最近更新 更多