【问题标题】:Specify priority between implict conversions指定隐式转换之间的优先级
【发布时间】:2021-04-07 04:10:14
【问题描述】:

我正在尝试找到一种方法来指定不同类型之间隐式转换的层次结构。

假设我有两种类型,每种类型都有一个函数重载:

struct A{};
struct B{};

void f(A const& a){}
void f(B const& b){}

现在我有另一个类,它可以隐式转换为AB

class C{
    A to_A()  const{return A{} ;}
    B to_B() const{return B{};}
    operator A() const{return to_A() ;}
    operator B() const{return to_B();}
};

事实上,由于模棱两可,我不能直接使用f

int main(){
    C c;
    f(c); // ambiguous, convert c to a, o c to b??
}

我们还可以说,在有选项的情况下,转换为 B 更明智。

有没有办法在struct C 中指定两者之一(AB)作为首选转换?,以便main 编译并等效于@987654334 @

完整代码在这里:https://godbolt.org/z/xvz71qEhK

我丢弃的可能的附近解决方案:

  1. 一种明显的方法是进行一次转换(到Aexplicit,但我希望这两种转换都是隐式的。因为还有其他函数(没有像 f 那样重载),其中两种转换都可以是隐式的。
  2. 在调用位置进行显式转换,main(){f(c.to_B());} 问题是我在通用模板函数中使用它,其中模板参数可以是 ABC
  3. 对于每个模棱两可的重载都创建一个新的重载f(C const& c){return f(c.to_B());},问题是我必须为每个可以采用AB 的重载函数执行此操作,而且我有很多。我可以拥有数十个类似f 的函数,其中一些具有多个参数(组合的数量激增)。

到目前为止我所做的尝试:虽然定义层次结构将有助于选择 首选 转换,并且叶类的运算符会有偏好,但它并没有消除歧义.

https://godbolt.org/z/fYj7PP5GE

template<class CRTP>
struct base_to_A{
    operator A()  const{
        return static_cast<CRTP const&>(*this).to_A() ;
    }
};

class C : public base_to_A<C>{
    A to_A() const{return A{};}
    B to_B() const{return B{};}
public:
    operator B() const{return to_B();}
};

public/protected/private 没有帮助。

【问题讨论】:

  • 对我来说,这似乎更像是一个设计问题,而不是一个实现/代码问题。还要记住,隐式转换往往会使代码更难阅读、理解和维护。
  • @Someprogrammerdude,是的,我从 AB 开始,然后添加了一个可以同时在语义上同时播放的新类型。如果CA 的关系是B 相等,则歧义是可以的,不知何故,我想让CB 的相关性比A 更相关(出于效率目的。

标签: c++ hierarchy implicit-conversion overload-resolution


【解决方案1】:

在 API 中使用变体,并在需要时分派到模板实现。

有一个知道你喜欢哪个的转换函数。

void f(std::variant<A,B,C> var1, std::variant<A,B,C> var2){
  return std::visit[&](auto& v1, auto& v2){
    f_impl(v1,v2);
  }, prefer_variant_convert<A,B>(var1), prefer_variant_convert<A,B>(var2) );
}

我们接收变体,运行代码将它们按顺序转换为首选的变体类型,然后生成指数数量的代码来解压缩变体。

或者,模板版本:

void f(auto var1, auto var2){
  return f_impl( prefer_convert<A,B>(var1), prefer_convert<A,B>(var2) );
}

如果prefer_convert&lt;Ts...&gt;(T0)Ts... 中,则返回T0,否则T0 可以转换为的Ts 中的第一个。应该不难写。

【讨论】:

  • 有趣的想法。我想这有一些运行时成本,我需要为f-like 的每个函数进行复制。一个工作的想法也许也考虑对std::variant&lt;A, B&gt;进行演员操作,我必须考虑一下。
  • @alfc 据我所知,第二个没有运行时成本。两者都生成用于转换的指数代码,但只有在以指数不同的方式调用时才会生成第二个代码。
【解决方案2】:

这并不能回答一般问题,它只是在特定情况下并考虑到某种设计。

适用于某些设计的部分解决方案: 如果C 可以单独和公开地以B 实现,则转换(实际上是转换为B)将是首选:

#include <cstdio>

struct A{};
struct B{};

void f(A const& a){std::puts("A");}
void f(B const& b){std::puts("B");}

class C : public B{
    A to_A() const{return A{} ;}
//    B to_B() const{return *this;} // not used
public:
    operator A() const{return to_A() ;}
//  operator B() const{return to_B();} // not needed, will never be called anyway
};

int main(){
    C c;
    f(c); // preferres B
}

https://godbolt.org/z/nnMPGbjeM

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-07-04
    • 2012-05-04
    • 1970-01-01
    • 2020-11-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-09-03
    相关资源
    最近更新 更多