【问题标题】:Overloading function templates in namespace std在命名空间 std 中重载函数模板
【发布时间】:2021-05-31 11:13:40
【问题描述】:

这里有关于函数特化的讨论:Will specialization of function templates in std for program-defined types no longer be allowed in C++20?

原则上我理解,重载而不是专门化更好。但是如何正确地重载 std 函数模板呢? 规范的答案似乎是:只需在您的自定义命名空间中重载,然后 ADL 就会启动。但是,如果涉及基本类型,这不起作用。非工作示例:

#include <cmath>

namespace X {

class Y {    };

Y sqrt(Y); 

double foo(double x) { return sqrt(x); }

}

该示例只会在没有 Y 的 sqrt 声明的情况下编译。可以通过在命名空间 std 中重载来解决此问题:

#include <cmath>

namespace X {
    class Y {    };
}

namespace std { X::Y sqrt(X::Y); }

namespace X {

double foo(double x) 
{
  return sqrt(x);
}

}

这段代码正是我想做的。但是我不确定标准是否允许这种重载。在 cppreference 我没有找到指向这个方向的提示。虽然 Walter E. Brown 的 paper 提出重载作为专业化的替代方案,但我不确定上述示例是否正确使用(论文没有给出任何示例)。

【问题讨论】:

  • foo 内调用std::sqrt(x);using std::sqrt 有什么问题?
  • 关于std::sqrt(x); 见下文。在你的代码中到处写using std::sqrt(sin,exp aso)只是为了重新激活基本类型的标准函数,感觉就像是一个语言缺陷。
  • #include &lt;cmath&gt; 不必将sqrt 等引入全局命名空间。您的实现出于某种原因这样做,但这不是语言所保证的。通常你应该到处调用std::sqrt,或者说using std::sqrt,不管你是否定义了自己的sqrt
  • 关于“见下文”,我从下面来,希望在这里得到一个提示,问题到底是什么。你似乎期待std::sqrt 是它不是的东西。你想要别的东西,所以你需要做一些额外的事情。这不是语言缺陷,如果std::sqrt 可以调用任何东西,我宁愿将其视为语言缺陷,但不能保证是std::sqrt
  • 您可能对P1292R0 Customization Point Functions感兴趣。

标签: c++ overloading std c++20 function-templates


【解决方案1】:

不允许将sqrt 的重载添加到std 命名空间。

但是,您可以使用完全限定名称,也可以通过添加 using std::sqrt 来提供 std::sqrt 首选项:

#include <cmath>

namespace X {

class Y {    };

Y sqrt(Y) { return {}; } 

double foo(double x) { 
    return std::sqrt(x); 
}

} // namespace X

template <typename T>
T bar(T x){
    using std::sqrt;
    return sqrt(x);
}

double baz(double x){
    using std::sqrt;
    return sqrt(x);
}

double moo(double x){
    return bar(x);
}

X::Y zoo(X::Y x){
    return bar(x);
}

请注意,std::sqrt 禁用 ADL,而 using std::sqrt 仍然允许 ADL 启动,例如 bar,它从 moozoo 调用。

【讨论】:

  • 这正是我害怕的答案。正如您所指出的,完全限定不适用于模板。使用 std::sqrt 编写数字代码变得很痛苦。所有这些问题只是,因为基本数据类型不会将 std 添加到它们的 ADL 命名空间。顺便说一句,前段时间我问了一个相关问题:stackoverflow.com/questions/64785956
  • @krzikalla “正如你所指出的,完全限定不适用于模板。”对不起,我不明白你的意思。
  • 抱歉,评论界面很难掌握。无论如何,谢谢你的回答。看来我必须在理论上使用using std::sqrt;。在实践中我必须违反标准。
  • @krzikalla 我有点不明白为什么你会看到问题。
  • @krzikalla 如果“到处使用 std::sqrt [...] 编写代码”让您担心,那么您可以编写一个包装器 my_sqrt ,可以在没有此类怪癖的情况下调用它。
【解决方案2】:

回答我自己的问题(但如果没有这里的讨论就不会找到它):

#include <cmath>

namespace X {
namespace details {

class Y {    };

Y sqrt(Y); 

} // ns details

using details::Y;

double foo(double x) { return sqrt(x); }

} // ns X

现在 foo 可以保持原样,并且可以使用 sqrt(Y)。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-11-17
    • 2022-01-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-03-19
    相关资源
    最近更新 更多