admin管理员组

文章数量:1022997

Snip:

#include <optional>

template <typename>
class MyOpt;

template <typename T>
MyOpt(T) -> MyOpt<T>;

template <typename T>
class MyOpt : private std::optional<T>
{

public:
    using std::optional<T>::optional;
    using std::optional<T>::operator=;
};


int main() {
    MyOpt o{42};

    o=52;

    return 0;
}

This code fails to compile with some ambiguity error:

error: ambiguous overload for 'operator=' (operand types are 'MyOpt' and 'int')

note: candidate: 'constexpr MyOpt& MyOpt::operator=(const MyOpt&)'

note: candidate: 'constexpr MyOpt& MyOpt::operator=(MyOpt&&)'

see

What is the reason for this failure and can this be "fixed"(c++20 at disposal), such that in the derived class I do not have to reimplement the assignment operator?

Snip:

#include <optional>

template <typename>
class MyOpt;

template <typename T>
MyOpt(T) -> MyOpt<T>;

template <typename T>
class MyOpt : private std::optional<T>
{

public:
    using std::optional<T>::optional;
    using std::optional<T>::operator=;
};


int main() {
    MyOpt o{42};

    o=52;

    return 0;
}

This code fails to compile with some ambiguity error:

error: ambiguous overload for 'operator=' (operand types are 'MyOpt' and 'int')

note: candidate: 'constexpr MyOpt& MyOpt::operator=(const MyOpt&)'

note: candidate: 'constexpr MyOpt& MyOpt::operator=(MyOpt&&)'

see https://godbolt./z/jvMMGh9Gn

What is the reason for this failure and can this be "fixed"(c++20 at disposal), such that in the derived class I do not have to reimplement the assignment operator?

Share Improve this question edited Nov 19, 2024 at 15:53 cigien 60.6k11 gold badges82 silver badges121 bronze badges asked Nov 19, 2024 at 12:45 JuergenJuergen 3,7417 gold badges37 silver badges71 bronze badges 5
  • the full compiler error message should inform you about the candidate overloads that lead to the ambiguity. Please include the error message in the question – 463035818_is_not_an_ai Commented Nov 19, 2024 at 12:49
  • 2 Related: Is it advisable to inherit from std::optional? – Thomas Weller Commented Nov 19, 2024 at 13:05
  • @463035818_is_not_an_ai I still do not understand why though, hence the question. Can this be "fixed"? – Juergen Commented Nov 19, 2024 at 13:12
  • Maybe you could be more specific about the version of operator= you pull in with the using statement? – Mark Ransom Commented Nov 19, 2024 at 15:57
  • Your code compiles without warning if you comment out the using .. operator= line. But I'm not sure what effect(s) that may have further down the road. – Adrian Mole Commented Nov 19, 2024 at 19:44
Add a comment  | 

1 Answer 1

Reset to default 2

The problem is that you are trying to invoke the inherited assignment operator that cannot be called for scalar types. This is the overload (4) in cppreference. Note the following quote about this "forwarding" assignment operator:

The function does not participate in overload resolution unless ... and at least one of the following is true:

  • T is not a scalar type;
  • std::decay_t<U> is not T.

In your case, T is int, which is a scalar type, and std::decay_t<U> is int as well, and therefore it is also T.

A simple demo of this cause is using a non-scalar type; then, everything works fine: https://godbolt./z/cbasffaEM.

A demo of the same problem with custom simplified optional: https://godbolt./z/KnojW8z91. If you comment out the requires clause, then, the problem disappears.

A relevant part of the libstdc++ implementation of std::optional::operator= is here.

If you wonder why there is that condition about scalar types, I recommend reading this question: Why does std::optional::operator=(U&&) require U to be a non-scalar type?


As for that ambiguity, if I understand things correctly, the assignment now involves creating a temporary and calling move assignment operator. The problem is that there are two move assignment operators that can be used (one implicitly defined for MyOpt and the other one inherited from std::optional). And the compiler cannot decide which one to use. Clang provides a more understandable error message about this issue: https://godbolt./z/8Yb1qPzT1.

With std::optional itself, there is no such ambiguity, since only its move assignment operator can be called.

Snip:

#include <optional>

template <typename>
class MyOpt;

template <typename T>
MyOpt(T) -> MyOpt<T>;

template <typename T>
class MyOpt : private std::optional<T>
{

public:
    using std::optional<T>::optional;
    using std::optional<T>::operator=;
};


int main() {
    MyOpt o{42};

    o=52;

    return 0;
}

This code fails to compile with some ambiguity error:

error: ambiguous overload for 'operator=' (operand types are 'MyOpt' and 'int')

note: candidate: 'constexpr MyOpt& MyOpt::operator=(const MyOpt&)'

note: candidate: 'constexpr MyOpt& MyOpt::operator=(MyOpt&&)'

see

What is the reason for this failure and can this be "fixed"(c++20 at disposal), such that in the derived class I do not have to reimplement the assignment operator?

Snip:

#include <optional>

template <typename>
class MyOpt;

template <typename T>
MyOpt(T) -> MyOpt<T>;

template <typename T>
class MyOpt : private std::optional<T>
{

public:
    using std::optional<T>::optional;
    using std::optional<T>::operator=;
};


int main() {
    MyOpt o{42};

    o=52;

    return 0;
}

This code fails to compile with some ambiguity error:

error: ambiguous overload for 'operator=' (operand types are 'MyOpt' and 'int')

note: candidate: 'constexpr MyOpt& MyOpt::operator=(const MyOpt&)'

note: candidate: 'constexpr MyOpt& MyOpt::operator=(MyOpt&&)'

see https://godbolt./z/jvMMGh9Gn

What is the reason for this failure and can this be "fixed"(c++20 at disposal), such that in the derived class I do not have to reimplement the assignment operator?

Share Improve this question edited Nov 19, 2024 at 15:53 cigien 60.6k11 gold badges82 silver badges121 bronze badges asked Nov 19, 2024 at 12:45 JuergenJuergen 3,7417 gold badges37 silver badges71 bronze badges 5
  • the full compiler error message should inform you about the candidate overloads that lead to the ambiguity. Please include the error message in the question – 463035818_is_not_an_ai Commented Nov 19, 2024 at 12:49
  • 2 Related: Is it advisable to inherit from std::optional? – Thomas Weller Commented Nov 19, 2024 at 13:05
  • @463035818_is_not_an_ai I still do not understand why though, hence the question. Can this be "fixed"? – Juergen Commented Nov 19, 2024 at 13:12
  • Maybe you could be more specific about the version of operator= you pull in with the using statement? – Mark Ransom Commented Nov 19, 2024 at 15:57
  • Your code compiles without warning if you comment out the using .. operator= line. But I'm not sure what effect(s) that may have further down the road. – Adrian Mole Commented Nov 19, 2024 at 19:44
Add a comment  | 

1 Answer 1

Reset to default 2

The problem is that you are trying to invoke the inherited assignment operator that cannot be called for scalar types. This is the overload (4) in cppreference. Note the following quote about this "forwarding" assignment operator:

The function does not participate in overload resolution unless ... and at least one of the following is true:

  • T is not a scalar type;
  • std::decay_t<U> is not T.

In your case, T is int, which is a scalar type, and std::decay_t<U> is int as well, and therefore it is also T.

A simple demo of this cause is using a non-scalar type; then, everything works fine: https://godbolt./z/cbasffaEM.

A demo of the same problem with custom simplified optional: https://godbolt./z/KnojW8z91. If you comment out the requires clause, then, the problem disappears.

A relevant part of the libstdc++ implementation of std::optional::operator= is here.

If you wonder why there is that condition about scalar types, I recommend reading this question: Why does std::optional::operator=(U&&) require U to be a non-scalar type?


As for that ambiguity, if I understand things correctly, the assignment now involves creating a temporary and calling move assignment operator. The problem is that there are two move assignment operators that can be used (one implicitly defined for MyOpt and the other one inherited from std::optional). And the compiler cannot decide which one to use. Clang provides a more understandable error message about this issue: https://godbolt./z/8Yb1qPzT1.

With std::optional itself, there is no such ambiguity, since only its move assignment operator can be called.

本文标签: inheritanceInherit assignment operator failure cStack Overflow