C++ C++;隐式构造函数转换后跟类型向上转换

C++ C++;隐式构造函数转换后跟类型向上转换,c++,C++,我有一个程序,它有一个基类Value,其中有多个子类(例如IntValue)继承自Value。每个类都有接受一个或多个参数的构造函数。下面是示例代码,显示了我希望能够执行的操作: #include <iostream> class Value { public: Value() {} virtual void print(std::ostream& os) const {} }; class IntValue: public Value { public: I

我有一个程序,它有一个基类
Value
,其中有多个子类(例如
IntValue
)继承自
Value
。每个类都有接受一个或多个参数的构造函数。下面是示例代码,显示了我希望能够执行的操作:

#include <iostream>

class Value {
public:
  Value() {}
  virtual void print(std::ostream& os) const {}
};

class IntValue: public Value {
public:
  IntValue(int val): val_(val) {}
  void print(std::ostream& os) const override { os << val_; }
private:
  int val_;
};

class VariableValue: public Value {
public:
  VariableValue(const std::string& name): name_(name) {}
  void print(std::ostream& os) const override { os << name_; }
private:
  const std::string name_;
};

void emit_add(const Value& lhs, const Value& rhs, std::ostream& os) {
  lhs.print(os);
  os << " + ";
  rhs.print(os);
  os << std::endl;
}

template <class ValueType>
void emit_add(const ValueType& lhs, const ValueType& rhs, std::ostream &os) {
  lhs.print(os);
  os << " + ";
  rhs.print(os);
  os << std::endl;
}

int main() {
  // all these work                                                                              
  emit_add<IntValue>(12, 13, std::cout); // implicit constructors                                
  emit_add<VariableValue>(std::string("x"), std::string("y"), std::cout); // implicit constructo\
rs                                                                                               
  emit_add(VariableValue(std::string("x")), IntValue(1), std::cout); // implicit upcasting       

  // this doesn't                                                                                
  emit_add(std::string("x"), 13, std::cout); // implicit constor + implicit upcasting            

  return -1;
}
我的理解是,编译器未能调用
VariableValue
的隐式构造函数,然后将其向上转换为type
Value
,但它显然可以分别执行这两种操作


可以强制编译器执行此操作吗?

一个
变量值
是一个
(因为继承是一个“是一个”关系),但
不是一个
变量值
(继承的“是一个”关系是单向的)

我想说的是,如果你有一个
VariableValue
对象,你可以很容易地在继承链上得到一个
Value
对象(或它的引用)。但是你不能从另一个方向,从
对象沿着继承链往下走,而不明确它

您需要显式构造一个
VariableValue
对象并将其传递给函数:

emit_add(VariableValue(x), 13, std::cout);
考虑以下作为反例:

现在,当编译器看到
emit\u add
接受两个
Value
s和
Value
时,这就起作用了,因为有合适的非显式构造函数接受
std::string
int

C++所提供的是根据给定的参数从基类推断派生类,如已/< 不过,您可以提供一个包装器来完成这项工作:

class Wrapper
{
    std::unique_ptr<Value> value;
public:
    Wrapper(int n) : value(new IntValue(n)) { }
    Wrapper(std::string) : value(new VariableValue(n)) { }
    friend std::ostream& operator<<(std::ostream& s, Wrapper const& w)
    {
        w.value->print(s);
        return s;
    }
};

如果您现在有一些要打印的自定义类型,只需提供一个
操作符
void emit\u add(const Value&lhs,
,然后以
std::string
作为第一个参数调用。如果重载std::string操作符(),可以强制编译器这是一个不好的继承用法。你的任务在C++中更容易使用,而不是继承。@ PulcMcKeZi:这里没有模板,只是普通的函数重载。边注:不要在打印输出中添加<代码> STD::EndoL< /Cord>。(你这样做了,代码>变量值< /C> >你不能摆脱,如果你想打印,例如:代码> name =“某个名字”<代码>(添加引号)…@ NigHalasMosier--我将按你所说的去做。老实说,这看起来像java程序员试图从C++中获得的整数、字符串等类似于java的尝试。对象,这几乎永远不会有好的结果。他并没有试图走错方向,错误在于期望编译器推断出一个没有出现在任何类型或函数签名中的类型。请参阅我所做的编辑。
emit\u add(VariableValue(x)、IntValue(13)、std::cout)
有效,但我很懒,我不想遍历1500多行代码,并将每次调用的参数包装到
emit\u add
。在原始问题的上下文中,根本不需要是类。几个函数重载就可以了。@PaulSanders如果函数很复杂,您可能不想一直重复代码。转换为中间表示(例如std::string)可以解决这个问题,但是如果这个中间表示不能满足新流(例如将二进制数据存储到文件中的流)的需要,将来可能会失败。此外,您可能需要大量重载(int+int、string+int、int+string、string+string,然后再次与双精度、无符号长自定义类配对,…)@Aconcagua完全正确。在实际代码中,我有4种不同类型的
s。这意味着4^2=16种不同的函数重载,每个函数重载的实现都完全相同。一定有更好的方法。@NicholasMosier您可以使用模板生成这些重载。为我的答案添加了一些行。@PaulSanders或答案变得越来越大,比如这个…看起来就像我们这里有一个xy问题。奇怪-为什么我没有认出我最喜欢的一个呢???
class Value
{
public:
    Value() { }
    Value(int) { }
    Value(std::string const&) { }
};

emit_add(x, 13, std::cout);
class Wrapper
{
    std::unique_ptr<Value> value;
public:
    Wrapper(int n) : value(new IntValue(n)) { }
    Wrapper(std::string) : value(new VariableValue(n)) { }
    friend std::ostream& operator<<(std::ostream& s, Wrapper const& w)
    {
        w.value->print(s);
        return s;
    }
};
void emit_add(Wrapper const& lhs, Wrapper const& rhs, std::ostream& os)
{
    os << lhs << " + " << rhs << std::endl;
}
void emit_add(Value const& x, Value const& y, std::ostream& os);

template <typename TX, typename TY>
void emit_add(TX&& x, TY&& y, std::ostream& os)
{
    emit_add
    (
            static_cast<Value const&>(TheValue<TX>(std::forward<TX>(x))),
            static_cast<Value const&>(TheValue<TY>(std::forward<TY>(y))),
            os
    );
}
template <typename T>
class TheValue : public Value
{
public:
    TheValue(T&& t)
            : val_(std::forward<T>(t))
    {  }
    void print(std::ostream& os) const override
    {
        os << val_;
    }
private:
    T val_;
};
using IntValue = TheValue<int>;
template <typename TX, typename TY>
void emit_add(TX&& x, TY&& y, std::ostream& os)
{
    std::cout << std::forward<TX>(x) << " + " << std::forward<TY>(y) << std::endl;
}
template <typename T>
class Point
{
    T m_x, m_y;
public:
    // constructors, calculations, ... (whatever you might need/find useful)
    friend ostream& operator<<(ostream& s, Point const& p)
    {
        s << '(' p.m_x << ", " << p.m_y << ')';
    }
};
template <typename T>
ostream& operator<<(ostream& s, Point<T> const& p)
{
    s << '(' p.x() << ", " << p.y() << ')';
}