我该如何设计来解决对概念和不完整类型的这种限制?
How can I design around this restriction on concepts and incomplete types?
我有这样的东西:
template <typename T>
requires ::std::movable<T> || ::std::copyable<T> || ::std::is_void_v<T>
class ValueWrapper
{
// various functions to do stuff with value
};
class Value {
public:
Value(Value const &) = delete;
Value &operator =(Value const &) = delete;
Value(Value &&other) noexcept : x_{other.x_} { other.x_ = -1; }
Value &operator =(Value &&other) {
Value tmp{::std::move(other)};
x_ = tmp.x_;
tmp.x_ = -1;
return *this;
}
ValueWrapper<Value> do_something() const; // Generates error related to incomplete type. :-(
private:
int x_;
};
当然,它不起作用,因为在编译器看到 do_something
声明时,Value
是一个不完整的类型,并且不可能测试一个不完整的类型是否是可移动或可复制,且不无效。
我应该如何设计?
我可以更改 ValueWrapper,这样各个成员函数就需要一些东西,而不是让 class 需要一些模板参数。但是,这似乎很迟钝,因为 class 的目的是包装可移动或可复制的东西。我可以将 do_something
移出 Value
class 并使其成为免费功能。但这似乎过于限制,并且对于任何 Value
class.
的方法来说可能不是一件明智的事情
这里还有没有这些缺点的其他设计选择吗?
在我目前关注的特定非抽象案例中,ValueWrapper
恰好类似于 Boost expected
,因此被用来表示错误或值 return.
编辑: 到目前为止,我最喜欢的答案涉及使用 auto
,并且要求函数定义以内联形式出现才能工作。如果你想让函数定义 而不是 内联,你可以做这堆体操。但是,真的很奇怪。
#include <concepts>
#include <utility>
template <typename T>
requires ::std::movable<T> || ::std::copyable<T> || ::std::is_void_v<T>
class ValueWrapper
{
// various functions to do stuff with value
};
namespace priv_ {
// We can forward declare a class without its member functions.
// But we can't forward declare a function without being able to
// fully name all of its types.
class Silly;
}
class Value {
public:
Value() : x_{-1} { }
Value(Value const &) = delete;
Value &operator =(Value const &) = delete;
Value(Value &&other) noexcept : x_{other.x_} { other.x_ = -1; }
Value &operator =(Value &&other) {
Value tmp{::std::move(other)};
x_ = tmp.x_;
tmp.x_ = -1;
return *this;
}
// inline here is basically documenting that we intend to give an
// inline definition later. We might want to say what the actual
// return type is too.
auto inline do_something() const;
private:
int x_;
// And we can friend a forward declared class so all of its member
// functions are basically member functions of this class (and
// hence have unrestricted access to all member functions and
// variables).
friend class priv_::Silly;
};
namespace priv_ {
class Silly {
public:
// And finally, now that the Value type is 'complete', we can
// use it as a parameter to the `ValueWrapper` template type.
static ValueWrapper<Value> p_do_something(Value const &v);
};
}
auto inline Value::do_something() const
{
// And now that the declaration for `p_do_something` has been
// seen, we can call it.
return priv_::Silly::p_do_something(*this);
}
ValueWrapper<Value> foo()
{
Value v;
return v.do_something();
}
如何在具有默认类型的模板方法中转换 do_something ()
?
我的意思是
template <typename U = Value>
ValueWrapper<U> do_something() const;
或
template <int..., typename U = Value>
ValueWrapper<U> do_something() const;
如果你想避免 do_something()
可以调用解释类型。
类型在成员函数定义中是完整的,所以我们可以使用推导的return类型:
auto do_something() const {
return ValueWrapper<Value>();
}
我有这样的东西:
template <typename T>
requires ::std::movable<T> || ::std::copyable<T> || ::std::is_void_v<T>
class ValueWrapper
{
// various functions to do stuff with value
};
class Value {
public:
Value(Value const &) = delete;
Value &operator =(Value const &) = delete;
Value(Value &&other) noexcept : x_{other.x_} { other.x_ = -1; }
Value &operator =(Value &&other) {
Value tmp{::std::move(other)};
x_ = tmp.x_;
tmp.x_ = -1;
return *this;
}
ValueWrapper<Value> do_something() const; // Generates error related to incomplete type. :-(
private:
int x_;
};
当然,它不起作用,因为在编译器看到 do_something
声明时,Value
是一个不完整的类型,并且不可能测试一个不完整的类型是否是可移动或可复制,且不无效。
我应该如何设计?
我可以更改 ValueWrapper,这样各个成员函数就需要一些东西,而不是让 class 需要一些模板参数。但是,这似乎很迟钝,因为 class 的目的是包装可移动或可复制的东西。我可以将 do_something
移出 Value
class 并使其成为免费功能。但这似乎过于限制,并且对于任何 Value
class.
这里还有没有这些缺点的其他设计选择吗?
在我目前关注的特定非抽象案例中,ValueWrapper
恰好类似于 Boost expected
,因此被用来表示错误或值 return.
编辑: 到目前为止,我最喜欢的答案涉及使用 auto
,并且要求函数定义以内联形式出现才能工作。如果你想让函数定义 而不是 内联,你可以做这堆体操。但是,真的很奇怪。
#include <concepts>
#include <utility>
template <typename T>
requires ::std::movable<T> || ::std::copyable<T> || ::std::is_void_v<T>
class ValueWrapper
{
// various functions to do stuff with value
};
namespace priv_ {
// We can forward declare a class without its member functions.
// But we can't forward declare a function without being able to
// fully name all of its types.
class Silly;
}
class Value {
public:
Value() : x_{-1} { }
Value(Value const &) = delete;
Value &operator =(Value const &) = delete;
Value(Value &&other) noexcept : x_{other.x_} { other.x_ = -1; }
Value &operator =(Value &&other) {
Value tmp{::std::move(other)};
x_ = tmp.x_;
tmp.x_ = -1;
return *this;
}
// inline here is basically documenting that we intend to give an
// inline definition later. We might want to say what the actual
// return type is too.
auto inline do_something() const;
private:
int x_;
// And we can friend a forward declared class so all of its member
// functions are basically member functions of this class (and
// hence have unrestricted access to all member functions and
// variables).
friend class priv_::Silly;
};
namespace priv_ {
class Silly {
public:
// And finally, now that the Value type is 'complete', we can
// use it as a parameter to the `ValueWrapper` template type.
static ValueWrapper<Value> p_do_something(Value const &v);
};
}
auto inline Value::do_something() const
{
// And now that the declaration for `p_do_something` has been
// seen, we can call it.
return priv_::Silly::p_do_something(*this);
}
ValueWrapper<Value> foo()
{
Value v;
return v.do_something();
}
如何在具有默认类型的模板方法中转换 do_something ()
?
我的意思是
template <typename U = Value>
ValueWrapper<U> do_something() const;
或
template <int..., typename U = Value>
ValueWrapper<U> do_something() const;
如果你想避免 do_something()
可以调用解释类型。
类型在成员函数定义中是完整的,所以我们可以使用推导的return类型:
auto do_something() const {
return ValueWrapper<Value>();
}