C ++隐式构造函数转换,然后进行类型转换

时间:2018-08-25 04:57:10

标签: c++

我有一个程序,该程序具有基类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;
}

当我尝试使用clang 9.1.0进行编译时,出现以下错误:

test.cpp:47:3: error: no matching function for call to 'emit_add'
  emit_add(std::string("x"), 13, std::cout); // implicit constor + implicit upcasting
  ^~~~~~~~
test.cpp:25:6: note: candidate function not viable: no known conversion from 'std::string' (aka
      'basic_string<char, char_traits<char>, allocator<char> >') to 'const Value' for 1st
      argument
void emit_add(const Value& lhs, const Value& rhs, std::ostream& os) {
     ^
test.cpp:33:6: note: candidate template ignored: deduced conflicting types for parameter
      'ValueType' ('std::__1::basic_string<char>' vs. 'int')
void emit_add(const ValueType& lhs, const ValueType& rhs, std::ostream &os) {
     ^
1 error generated.

我的理解是,编译器无法为VariableValue调用隐式构造函数,然后将其向上转换为Value类型,但显然可以单独执行这两个操作。 / p>

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

2 个答案:

答案 0 :(得分:2)

VariableValueValue(因为继承是“是”关系),但是Value不是VariableValue(“继承是单向的)。

我要说的是,如果您有一个VariableValue对象,则可以轻松地在继承链中向上获取一个Value对象(或它的引用)。但是,如果没有明确说明,您就无法沿着Value对象的继承链进行操作。

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

emit_add(VariableValue(x), 13, std::cout);

答案 1 :(得分:1)

将以下内容视为计数器-示例:

watch

现在这将会起作用,因为编译器发现var process = processBuilder.start(); var stream = process.getInputStream(); var input = new StringBuilder(); int n; //while (isOpenIO) { while ((n = stream.read()) != -1) input.append((char) n); if (input.length() > 0) System.out.println("NON EMPTY"); if (input.length() > 3) { String text = input.substring(input.indexOf("\n") + 1, input.length() - 1); String modified = text.replaceAll("\\B(?=(\\d{3})+(?!\\d))", ","); System.out.println("RAM: " + modified + " Mb")); } //} 接受两个class Value { public: Value() { } Value(int) { } Value(std::string const&) { } }; emit_add(x, 13, std::cout); ,并且emit_add拥有适当的非显式构造函数,接受ValueValue

不提供C ++的功能是根据给定的参数从基类中推断出派生的类,就像已经Some programmer dude denoted一样。

不过,您可以提供包装器来为您完成这项工作:

std::string

是否真的比直接指定类型(如某些程序员伙计所做的那样)更好?另一方面,如上所述(使用int),您现在可以执行以下操作:

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;
    }
};

这也更舒适...

另一种方法可能是重载模板:

operator<<

上面的强制转换是必需的,否则模板本身将是更好的匹配,并且不会选择非模板,从而导致无休止的递归。我将具体值转换为以下模板:

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>;

如果您现在要打印一些自定义类型,只需为其提供Value重载,例如以下要点示例:

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;
}

旁注:上面的Friend运算符对于您自己的数据类型很方便(它仍然定义了 freestanding 函数),即使您这样做:

operator<<

在类外部(您仍然可以在内部将其声明为朋友;当然,无论是否允许,都不能使用私有成员,因此,您假设上面{{1} }和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 << ')'; } }; 是公众获得者。