I have a class with both a copy constructor and a constructor taking a std::reference_wrapper
:
#include <functional>
#include <iostream>
class Class {
public:
Class() {
std::cout << "Class()" << std::endl;
}
Class(Class const &) {
std::cout << "Class(Class const &)" << std::endl;
}
Class(std::reference_wrapper<Class>) {
std::cout << "Class(std::reference_wrapper<Class>)" << std::endl;
}
Class(std::reference_wrapper<const Class>) {
std::cout << "Class(std::reference_wrapper<const Class>)" << std::endl;
}
};
int main() {
Class a;
Class b = a;
Class c = std::ref(a);
Class d = std::cref(a);
}
When compiled normally (g++ --std=c++17 test.cpp
) this works as desired, calling the four constructors in sequence:
$ ./a.exe
Class()
Class(Class const &)
Class(std::reference_wrapper<Class>)
Class(std::reference_wrapper<const Class>)
However, compiling with -pedantic
(i.e., g++ --std=c++17 -pedantic test.cpp
) results in the following error (and another equivalent one for the std::cref
):
test.cpp:23:22: error: conversion from 'std::reference_wrapper<Class>' to 'Class' is ambiguous
Class c = std::ref(a);
^
note: candidate: std::reference_wrapper<_Tp>::operator _Tp&() const [with _Tp = Class]
note: candidate: Class::Class(std::reference_wrapper<Class>)
Why is this (i.e., how am I violating the standard, answered in Conversion constructor vs. conversion operator: precedence), and how do I achieve the result without -pedantic
in a standard-conforming manner?
$ g++ --version
g++.exe (Rev1, Built by MSYS2 project) 7.2.0
how do I achieve the result without -pedantic in a standard-conforming manner?
Create overloads with exact match, in your case:
Class(std::reference_wrapper<Class>&&) {
std::cout << "Class(std::reference_wrapper<Class>)" << std::endl;
}
Class(std::reference_wrapper<const Class>&&) {
std::cout << "Class(std::reference_wrapper<const Class>)" << std::endl;
}
Demo
Define the constructors as explicit
to avoid the conflict with the conversion operator operator T& () const
defined in the std::reference_wrapper
class:
#include <functional>
#include <iostream>
class Class {
public:
Class() {
std::cout << "Class()" << std::endl;
}
Class(Class const &) {
std::cout << "Class(Class const &)" << std::endl;
}
explicit Class(std::reference_wrapper<Class>) {
std::cout << "Class(std::reference_wrapper<Class>)" << std::endl;
}
explicit Class(std::reference_wrapper<const Class>) {
std::cout << "Class(std::reference_wrapper<const Class>)" << std::endl;
}
};
int main() {
Class a;
Class b = a;
Class c = std::ref(a); // call Class(Class const &) trough the conversion operator
Class d(std::ref(a)); // call Class(std::reference_wrapper<Class>)
Class e = std::cref(a); // call Class(Class const &) trough the conversion operator
Class f(std::cref(a)); // call Class(std::reference_wrapper<const Class>)
}
Alternatively remove the constructor overloads 3 and 4 to always use the copy constructor Class(Class const &)
.
You get no errors without the pedantic option because GCC gives precedence to copy constructor over conversion operators, but this is non part of the the standard, where no precedence is defined, hence the conflict.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With