【问题标题】:Is this an elegant way to overload a static member function that provides the same interface as a non static member function?这是重载提供与非静态成员函数相同接口的静态成员函数的一种优雅方式吗?
【发布时间】:2019-11-29 10:07:07
【问题描述】:

考虑以下具有模板变量并使用模板别名和自动类型推导的非类模板。

template<typename T>
using Type = T;

using TypeA = Type<int>;
using TypeB = Type<double>;

class Foo {
private:
    template<typename T>
    static Type<T> type_;
public:
    template<typename T>
    explicit Foo( Type<T> type ) { type_<T> = type; }

    // non static member
    template<typename T>
    auto bar() { return type_<T>; }

    // static member
    template<typename T>
    static auto bar(T _x_ = 0) { return type_<T>; }
};

以及使用它的程序:

// has to be defined in some cpp file.
template<typename T>
Type<T> Foo::type_;

int main() {
     TypeA a{ 7 };
     TypeB b{ 3.41 };

     Foo f1( a );
     Foo f2( b );

     auto x = Foo::bar<TypeA>();
     auto y = Foo::bar<TypeB>();

     std::cout << "static auto f1: " << x << '\n';
     std::cout << "static auto f2: " << y << '\n';

     std::cout << "member f1: " << f1.bar<TypeA>() << '\n';
     std::cout << "member f2: " << f2.bar<TypeB>() << '\n';

     return 0;
};

输出

static auto f1: 7
static auto f2: 3.41
member f1: 7
member f2: 3.41

在类声明中;我在静态版本中使用参数T 并将其默认为0,以便可以在没有任何参数的情况下调用它。如果不添加它,那么将无法重载没有参数或参数列表具有相同参数的静态和非静态成员函数。

这会被认为是快速修复还是破解,或者这是一种能够为具有相同名称和功能的静态和非静态成员函数提供相同类型接口的可能方式?

函数参数或参数是一个虚拟参数,对内部值绝对没有任何作用。

回来并再次阅读此内容后,我可以看出其中的一些困惑来自于我忽略了这更多地与变量模板的使用和能够访问它们有关的事实。

所以我认为真正的问题应该是:关于变量模板成员并且它们必须是静态的,通过成员函数访问它们的首选方式是什么:通过静态或非静态,或者没有偏好这个选择留给程序员?

最后一件事;是否有任何不可预见的问题可能导致这种设计模式 - 接口的未来后果?

【问题讨论】:

  • “你在一个曲折的小通道迷宫里,都一样。”
  • 可以使用f1.bar&lt;TypeA&gt;() 语法调用静态成员函数。无需声明非静态版本(只要您不想创建指向该函数的成员指针。)
  • 我什至不明白你在这里做什么。返回类型将是Type&lt;Type&lt;int&gt;&gt;Type&lt;Type&lt;double&gt;&gt;。这就是你想要的吗?如果你这样做了,那似乎很奇怪。
  • 您使用的是哪个编译器?尝试编译时出现“模糊调用”类型错误。
  • C++ Static member method call on class instance 的可能重复项。或者可能不是,这取决于这种“相同类型的接口”的预期用途。

标签: c++ static overloading c++17 template-variables


【解决方案1】:

这被认为是一种快速修复或破解,还是能够为同名的静态和非静态成员函数提供相同类型的接口的正确方法?

没有适当的方法来实现不适当的目标。除此之外,您的版本不提供相同的界面。非静态版本需要显式模板参数,而静态版本可以使用推导,如f1.bar(1)。我不确定我是否建议在这种情况下使用推论(因为代码更神秘),但提供了这种可能性。你的不正当目标甚至没有达到。

如果函数具有相同的功能(如您的示例中),那么非静态版本是毫无意义的开销。只提供静态版本,如果有人想从对象调用它,那没关系。

如果函数在功能上不相同(也许您的示例过于简单?),那么给它们取相同的名称是一个坏主意。这包括非静态版本可以根据*this 返回不同值的情况。至少在这种情况下,静态版本应该重命名为 bar_no_object() 之类的东西,以区别于依赖对象的版本。

最后一件事;是否有任何不可预见的问题可能导致这种类型的设计模式 - 界面的未来后果?

好吧,基本上你是在为自己的混乱做准备。其他人都会期望 Foo::bar()Foo{}.bar() 调用相同的(静态)函数,而您正试图打破它。

注意编译器的消息。其他人的这种期望是您无法“重载没有参数或参数列表具有相同参数的静态和非静态成员函数”的原因。你试图制造歧义,编译器阻止了你。有充分的理由。在问if your workaround is valid 之前,也许你应该问一下编译器阻止你的原因?

【讨论】:

  • 我理解为什么编译器阻止我重载具有相同签名的静态和非静态函数。我只是在试验变量模板和模板别名,当我开始将变量模板添加为类成员时,它迫使我将变量声明为静态。我能够编写一个访问函数来通过静态或非静态版本检索值。然后我发现如果我给静态版本一个 dummy 参数,它在内部什么都不做,那么我可以从一个或两个函数中检索值。
  • @FrancisCugler 换句话说,您发现非静态版本可以做静态版本可以做的所有事情,但可以在更少的上下文中使用(静态版本可以使用的上下文的严格子集)用过的)。它严格不如静态版本。有没有理由保留它。然而你想保留它。结果是杂乱无章的代码,没有任何好处。不要那样做。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-05-03
  • 1970-01-01
  • 2011-05-31
  • 1970-01-01
  • 1970-01-01
  • 2022-01-22
  • 1970-01-01
相关资源
最近更新 更多