Constructing a value through two implicit construc

2019-02-18 17:08发布

TLDR: I have two templatized classes Outer and Inner. Inner<X> can be implicitly constructed from X, and Outer<Y> can be implicitly constructed from Y. Should Outer<Inner<X>> = X() work?

More details:

Suppose I have the following two classes:

template<typename T> 
class Inner {
  public: 
    Inner(const T& value) {}
    Inner(T&& value) {}
};

template<typename T>
class Outer {
  public:
    Outer(const T& value) {}
    Outer(T&& value) {}
};

Consider the following function:

struct SomeType{};
Outer<Inner<SomeType>> DoSomethingFails() {
  SomeType value;
  return value;      
}

g++ complains:

no viable conversion from 'SomeType' to 'Outer<Inner<SomeType> >'
note: candidate constructor not viable: no known conversion from 'SomeType' to 'const Inner<SomeType> &' for 1st argument

But if I do the following instead:

Outer<Inner<SomeType>> DoSomethingWorks() {
  SomeType value;
  return Inner<SomeType>(value);      
}

It works. Is it reasonable to expect DoSomethingFails to work? If not, why? And can the code be changed in a way that DoSomethingFails works?

1条回答
一夜七次
2楼-- · 2019-02-18 17:49

Your first example requires two user defined conversions to compile — SomeType -> Inner -> Outer. However, at most one user defined conversion can be applied implicitly.

Quoting N3337, §12.3 [class.conv]

1   Type conversions of class objects can be specified by constructors and by conversion functions. These conversions are called user-defined conversions and are used for implicit type conversions (Clause 4), for initialization (8.5), and for explicit type conversions (5.4, 5.2.9).

4   At most one user-defined conversion (constructor or conversion function) is implicitly applied to a single value.


If the goal is to avoid having to mention Inner<SomeType> in the return statement, you can use list initialization.

Outer<Inner<SomeType>> DoSomethingWorks2() {
  SomeType value;
  return {std::move(value)};
}
查看更多
登录 后发表回答