在转换运算符不明确的情况下进行选择

时间:2018-09-25 15:26:23

标签: c++

我有两种类型:

struct A { };
struct B { };

我有采用AB的函数:

void fnA(A); // there are a lot of these functions
void fnB(B);

我有一个类型,可以转换为AB

struct Foo {
    operator A();
    operator B();
};

所以我可以打电话给fnAfnB

fnA(Foo()); // fine
fnB(Foo());

现在,我有重载的函数:

void fn(A);
void fn(B);

我不能用Foo打电话给他们,因为它不明确:

fn(Foo()); // ambiguous, which fn is called

在这种情况下,我想打电话给fn(A)

我可以添加第三个fn重载:

inline void fn(Foo foo) {
    fn(A(foo));
}

但是我不喜欢这种方式,因为我有很多fn函数,并且我不想大幅增加函数的数量(我分散了fn类的函数到处都会发生这种变化,这会增加界面的大小,这对我来说是不利的,因为我的界面已经很大了。

另一种解决方案(如果没有更好的选择,我将选择它)是对Foo使用继承:

struct Foo: A {
    operator B();
};

在这种情况下,编译器将选择为fn(A)调用fn(Foo()),这不再是模棱两可了。但是我对这种解决方案不满意,因为Foo并不是真正的A,这是对继承的滥用(另一方面,它比以前的解决方案更好,因为它可以在本地解决问题,我不必添加很多不需要的fn函数)。

还有其他解决方法吗?

注意:我想有一个不涉及显式转换的解决方案,我想能够编写fnA(Foo())fnB(Foo())fn(Foo())

3 个答案:

答案 0 :(得分:6)

如何使用精美的模板包装器

void fn_impl(A);
void fn_impl(B);

template<typename x_AB = A, typename x_Foo = Foo>
void fn(x_Foo && foo)
{
    return fn_impl(static_cast<x_AB>(foo));
}

fn(Foo()); // calls fn_impl(A);
fn<B>(Foo()); // calls fn_impl(B);

online compiler

答案 1 :(得分:4)

我认为您可以通过使每个fn(B)重载模板来实现所需的行为:

void fn(A);

template<class = void>
void fn(B);

void bar()
{
    fn(Foo());
    fn(A());
    fn(B());
}

这将导致重载分辨率在考虑模板函数之前选择fn(A)。必要的工作等于将template<class = void>放在每个函数的每个B重载之前(并且,如果声明与定义分开,则将所有此类函数转换为模板专用化)。

Demo

答案 2 :(得分:1)

只需将Foo强制转换为适当的类型:

#include <iostream>
#include <string>

struct A { };
struct B { };

struct Foo {
    operator A() { return A{}; };
    operator B() { return B{}; };
};

void fn(A) { std::cout << "A"; }
void fn(B) { std::cout << "B"; }

int main()
{
  fn(static_cast<A>(Foo()));
}