是否可以在允许显式转换的同时弃用隐式转换?

Nat*_*son 29 c++ type-conversion

假设我有一个简单的Duration类:

class Duration
{
    int seconds;
public:
    Duration(int t_seconds) : seconds(t_seconds) { }
};

int main()
{
    Duration t(30);
    t = 60;
}
Run Code Online (Sandbox Code Playgroud)

我决定我不喜欢能够从 隐式转换intDuration。我可以制作构造函数explicit

class Duration
{
    int seconds;
public:
    explicit Duration(int t_seconds) : seconds(t_seconds) { }
};

int main()
{
    Duration t(30); // This is fine, conversion is explicit
    t = 60; // Doesn't compile: implicit conversion no longer present for operator=
}
Run Code Online (Sandbox Code Playgroud)

但是,如果我不想立即破坏所有隐式转换为 的调用代码怎么办Duration?我想要的是这样的:

class Duration
{
    int seconds;
public:
    [[deprecated]]
    Duration(int t_seconds) : seconds(t_seconds) { }

    explicit Duration(int t_seconds) : seconds(t_seconds) { }
};

int main()
{
    Duration t(30); // Compiles, no warnings, uses explicit constructor
    t = 60; // Compiles but emits a deprecation warning because it uses implicit conversion
}
Run Code Online (Sandbox Code Playgroud)

这将允许现有代码在编译时识别当前依赖隐式转换的任何位置,因此可以将它们重写为使用显式转换(如果需要)或重写为具有正确的行为(如果不需要)。

然而这是不可能的,因为我不能Duration::Duration(int)超载Duration::Duration(int)

有没有一种方法可以实现类似这种效果的效果,而不是“使转换明确,接受调用代码在编写适当的更改之前不会编译”?

康桓瑋*_*康桓瑋 34

您可以变成Duration(int t_seconds)一个可以接受int并将其设置为已弃用的模板函数。

#include<concepts>

class Duration {
  int seconds;
public:
  template<std::same_as<int> T>
  [[deprecated("uses implicit conversion")]]
  Duration(T t_seconds) : Duration(t_seconds) { }
  
  explicit Duration(int t_seconds) : seconds(t_seconds) { }
};
Run Code Online (Sandbox Code Playgroud)

如果允许t = 0.6,只需将 更改same_asconvertible_to

演示。

  • @TheScore它将它用作[约束](https://en.cppreference.com/w/cpp/language/constraints#Concepts)。也就是说,必须满足“std::same_as&lt;T, int&gt;”才能使“T”成为有效参数。 (4认同)
  • 一旦这给了我尝试的想法,SFINAE 版本似乎并不[那么糟糕](https://godbolt.org/z/3TnP31rh7)。很好的建议! (3认同)
  • 降级到 [C++17](https://godbolt.org/z/aoofo85sY) 和 [C++11](https://godbolt.org/z/EbE949nxc)。 (3认同)
  • 请注意,此解决方案将破坏如下代码:`Duration 分钟() { return {60}; }` 原因是在复制列表初始化中,重载决策是在不考虑“显式”的情况下完成的,如果实际选择了显式构造函数,则程序是格式错误的。 (2认同)
  • 即使只是 `template&lt;typename = void&gt; [[deprecated]] Duration(int t_seconds) : Duraction(t_seconds) {}` 也可以工作,因为显式初始化总是会选择非模板初始化 (2认同)