I still do not understand the behavior of std::optional
in the following code:
class A
{
public:
// A(int x, int y) : x(x), y(y) {} // always compiles
private:
A(int x, int y) : x(x), y(y) {} // does not compile if using o.emplace(x, y)
friend std::optional<A> makeA(int x, int y);
int x, y;
};
std::optional<A> makeA(int x, int y)
{
std::optional<A> o;
if (x != 0 && y != 0) {
return A(x, y);
// o.emplace(x, y);
}
return o;
}
If I make the constructor of A
as public then in makeA(...)
I can use either return A(x, y)
or o.emplace(x, y)
, boths compile.
But if I make the constructor of A
private then o.emplace(x, y)
will not compile. As far as I understand from reading the message error of templates:
error: no matching function for call to ‘std::optional<A>::emplace(int&, int&)’
70 | o.emplace(x, y);
| ~~~~~~~~~^~~~~~
In file included from test.cpp:2:
/usr/include/c++/11/optional:871:9: note: candidate: ‘template<class ... _Args> std::enable_if_t<is_constructible_v<_Tp, _Args ...>, _Tp&> std::optional<_Tp>::emplace(_Args&& ...) [with _Args = {_Args ...}; _Tp = A]’
871 | emplace(_Args&&... __args)
| ^~~~~~~
/usr/include/c++/11/optional:871:9: note: template argument deduction/substitution failed:
In file included from /usr/include/c++/11/bits/move.h:57,
from /usr/include/c++/11/bits/stl_pair.h:59,
from /usr/include/c++/11/bits/stl_algobase.h:64,
from /usr/include/c++/11/memory:63,
from test.cpp:1:
/usr/include/c++/11/type_traits: In substitution of ‘template<bool _Cond, class _Tp> using enable_if_t = typename std::enable_if::type [with bool _Cond = false; _Tp = A&]’:
/usr/include/c++/11/optional:871:2: required by substitution of ‘template<class ... _Args> std::enable_if_t<is_constructible_v<A, _Args ...>, A&> std::optional<A>::emplace<_Args ...>(_Args&& ...) [with _Args = {int&, int&}]’
test.cpp:70:14: required from here
/usr/include/c++/11/type_traits:2579:11: error: no type named ‘type’ in ‘struct std::enable_if<false, A&>’
2579 | using enable_if_t = typename enable_if<_Cond, _Tp>::type;
| ^~~~~~~~~~~
In file included from test.cpp:2:
/usr/include/c++/11/optional:883:9: note: candidate: ‘template<class _Up, class ... _Args> std::enable_if_t<is_constructible_v<_Tp, std::initializer_list<_Up>&, _Args ...>, _Tp&> std::optional<_Tp>::emplace(std::initializer_list<_Up>, _Args&& ...) [with _Up = _Up; _Args = {_Args ...}; _Tp = A]’
883 | emplace(initializer_list<_Up> __il, _Args&&... __args)
| ^~~~~~~
/usr/include/c++/11/optional:883:9: note: template argument deduction/substitution failed:
test.cpp:70:14: note: mismatched types ‘std::initializer_list<_Tp>’ and ‘int’
70 | o.emplace(x, y);
the class A
is not constructible. But how is it possible?
I still do not understand the behavior of std::optional
in the following code:
class A
{
public:
// A(int x, int y) : x(x), y(y) {} // always compiles
private:
A(int x, int y) : x(x), y(y) {} // does not compile if using o.emplace(x, y)
friend std::optional<A> makeA(int x, int y);
int x, y;
};
std::optional<A> makeA(int x, int y)
{
std::optional<A> o;
if (x != 0 && y != 0) {
return A(x, y);
// o.emplace(x, y);
}
return o;
}
If I make the constructor of A
as public then in makeA(...)
I can use either return A(x, y)
or o.emplace(x, y)
, boths compile.
But if I make the constructor of A
private then o.emplace(x, y)
will not compile. As far as I understand from reading the message error of templates:
error: no matching function for call to ‘std::optional<A>::emplace(int&, int&)’
70 | o.emplace(x, y);
| ~~~~~~~~~^~~~~~
In file included from test.cpp:2:
/usr/include/c++/11/optional:871:9: note: candidate: ‘template<class ... _Args> std::enable_if_t<is_constructible_v<_Tp, _Args ...>, _Tp&> std::optional<_Tp>::emplace(_Args&& ...) [with _Args = {_Args ...}; _Tp = A]’
871 | emplace(_Args&&... __args)
| ^~~~~~~
/usr/include/c++/11/optional:871:9: note: template argument deduction/substitution failed:
In file included from /usr/include/c++/11/bits/move.h:57,
from /usr/include/c++/11/bits/stl_pair.h:59,
from /usr/include/c++/11/bits/stl_algobase.h:64,
from /usr/include/c++/11/memory:63,
from test.cpp:1:
/usr/include/c++/11/type_traits: In substitution of ‘template<bool _Cond, class _Tp> using enable_if_t = typename std::enable_if::type [with bool _Cond = false; _Tp = A&]’:
/usr/include/c++/11/optional:871:2: required by substitution of ‘template<class ... _Args> std::enable_if_t<is_constructible_v<A, _Args ...>, A&> std::optional<A>::emplace<_Args ...>(_Args&& ...) [with _Args = {int&, int&}]’
test.cpp:70:14: required from here
/usr/include/c++/11/type_traits:2579:11: error: no type named ‘type’ in ‘struct std::enable_if<false, A&>’
2579 | using enable_if_t = typename enable_if<_Cond, _Tp>::type;
| ^~~~~~~~~~~
In file included from test.cpp:2:
/usr/include/c++/11/optional:883:9: note: candidate: ‘template<class _Up, class ... _Args> std::enable_if_t<is_constructible_v<_Tp, std::initializer_list<_Up>&, _Args ...>, _Tp&> std::optional<_Tp>::emplace(std::initializer_list<_Up>, _Args&& ...) [with _Up = _Up; _Args = {_Args ...}; _Tp = A]’
883 | emplace(initializer_list<_Up> __il, _Args&&... __args)
| ^~~~~~~
/usr/include/c++/11/optional:883:9: note: template argument deduction/substitution failed:
test.cpp:70:14: note: mismatched types ‘std::initializer_list<_Tp>’ and ‘int’
70 | o.emplace(x, y);
the class A
is not constructible. But how is it possible?
2 Answers
Reset to default 7std::optional<A>::emplace
forwards its parameters to A
's constructor. The constructor is private, hence not accesible by emplace
. You declared makeA
to be a friend
but that does not transitively apply to other functions called by makeA
. Hence, the error.
You can call the constructor directly because makeA
is declared a friend
of A
. Hence it can access the private constructor.
Small extension to existing answer. To overcome this issue you can use lock key pattern:
class A {
class LockKey {
friend std::optional<A> makeA(int x, int y);
private:
LockKey() { }
LockKey(const LockKey&) = default;
};
public:
A(int x, int y, const LockKey&)
: x(x)
, y(y)
{
}
private:
friend std::optional<A> makeA(int x, int y);
int x, y;
};
std::optional<A> makeA(int x, int y)
{
std::optional<A> o;
if (x != 0 && y != 0) {
#ifndef USE_EMPLACE
return A(x, y, A::LockKey {});
#else
o.emplace(x, y, A::LockKey {});
#endif
}
return o;
}
https://godbolt.org/z/sY4z4vWd9
emplace
to be some kind of "magic" that doesnt need to call the constructor, or maybe something else ... – 463035818_is_not_an_ai Commented Feb 5 at 16:27return A(...)
works theno.emplace(...)
works (I suppose that sincemakeA
is a friend then anything under its context is also friend ofA
). – Ta Thanh Dinh Commented Feb 5 at 16:30