Concepts TS を使用して、データの制約を支援したいと考えていました。p0121r0で説明したコンセプトについてお話しします。テストには GCC 6.2 を使用しています。
次の簡単なコードを見てください。
template<typename T>
concept bool test_is_available = requires(T t) {
t.test;
{ t.test++ };
{ t.test-- };
};
template<test_is_available T>
struct Tester
{
T t;
};
インクリメントおよびデクリメント可能なtestプロパティを持つ型を struct Tester に渡す必要があります。良い。
struct A
{
unsigned test;
}
Tester<A> a;
期待どおりに動作します。明らかに、次のものは機能しません。
struct B
{
std::string test;
};
struct C
{
unsigned not_test;
};
Tester<B> b; // error: test++ and test-- are ill formed
Tester<C> c; // error: no test available
さて、本当の質問: 次のものが機能しないのはなぜですか?
class D
{
unsigned test;
};
Tester<D> d; // error: private???
stdペーパーを掘り下げようとしましたが、std自体にこの可能性がない場合、コンパイラが正しく動作していない場合、この動作が予想されることを理解できません...
または、一種の友情を宣言する必要があるかもしれませんが、それがポイントになる可能性はありますか? これは、概念の制約がアクセサーによって制約される必要がない状況です...
ここで何が起こっているかについてのアイデアはありますか?
編集: 簡単な例で問題を理解するのは必ずしも容易ではありません。これはもう少し複雑ですが、実際のケースにより似ています。
#include <cassert>
#include <utility>
template<typename T>
concept bool Countable = requires(T t) {
t.counter;
{ ++t.counter };
{ --t.counter };
//{ t.load(auto&&...) } -> void; <-- I am not sure how to handle this
{ t.unload() } -> void;
};
template<Countable T>
class Scoper
{
public:
template<typename... Args>
Scoper(T& t, Args... args) : m_t(&t)
{
++t.counter;
t.load(std::forward<Args>(args)...);
}
~Scoper()
{
--m_t->counter;
m_t->unload();
}
private:
T* m_t;
};
class Scopeable
{
public:
unsigned getCounter() const
{
return counter;
}
//private:
//template<Countable> friend class Scoper; <-- does not work
void load(char, int) {}
void unload() {}
unsigned counter = 0;
};
int main()
{
Scopeable scopeable;
assert(scopeable.getCounter() == 0);
{
Scoper<Scopeable> scoper(scopeable, 'A', 2);
assert(scopeable.getCounter() == 1);
}
assert(scopeable.getCounter() == 0);
}
ご覧のとおり、counter、load、およびunloadは非公開/保護する必要があり、 Scoperからのみアクセスする必要があることは明らかです。抽象基本クラスを使用すると、 counterとunloadのみを制約できますが、 loadはできません (ご覧のとおり、正しい構文を処理する方法がわかりません...)。
これで答えが変わることはないかもしれませんが、問題はおそらく少しきれいになります。