最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

c++ - Return std::optional<T> where T's constructor is private - Stack Overflow

programmeradmin0浏览0评论

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?

Share Improve this question edited Feb 5 at 16:15 Ta Thanh Dinh asked Feb 5 at 16:07 Ta Thanh DinhTa Thanh Dinh 6611 gold badge6 silver badges13 bronze badges 5
  • please post the code that has the error together with the compiler error message. Asking us to modify the code before we can see the error is a source of confusion and misunderstanding (even if the modification might seem minor and/or obvious) – 463035818_is_not_an_ai Commented Feb 5 at 16:10
  • why would you think the constructor is not required: godbolt.org/z/55331EjM9 – Oersted Commented Feb 5 at 16:12
  • godbolt.org/z/WsW6WdP19 – Marek R Commented Feb 5 at 16:19
  • i am still curious what else you expected and why. My guess is that you expected 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:27
  • 1 I expected that if return A(...) works then o.emplace(...) works (I suppose that since makeA is a friend then anything under its context is also friend of A). – Ta Thanh Dinh Commented Feb 5 at 16:30
Add a comment  | 

2 Answers 2

Reset to default 7

std::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

发布评论

评论列表(0)

  1. 暂无评论