I wanted to use the Concepts TS to help me with data constraining. I will talk about the Concepts discussed in p0121r0 and I am using GCC 6.2 for the tests.
Take this simple piece of code:
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;
};
I have to pass to the struct Tester a type with the test property, which is incrementable and decrementable. Good.
struct A
{
unsigned test;
}
Tester<A> a;
Works as expected. Obviously, the following one will not work:
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
Now, the real question: why the following one is not working?
class D
{
unsigned test;
};
Tester<D> d; // error: private???
I tried to dig into the std paper, but I am not able to understand this behaviour is expected, if the std itself is missing this possibility, if the compiler is not working correctly...
Or maybe I need to declare a sort of friendship, but what could be the point? This is a situation in which the concepts constraints needs not to be constrained by accessors...
Any ideas of what is happening here?
EDIT: It is not always easy to give the idea of the problem with a simple example. This one is a bit more complex, but more similar to the real case:
#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);
}
As you can see, it is obvious that counter, load and unload have to be private/protected, and that they only has to be accessed from Scoper. If I use an abstract base class I can only constraint counter and unload, but not load (for which, as you can see, I do not know how to handle the correct syntax...).
Maybe this will not change your answers, but the problem is probably a little bit cleaner.
This concept:
template<typename T>
concept bool test_is_available = requires(T t) {
t.test;
{ t.test++ };
{ t.test-- };
};
requires that T
have a publicly accessible member test
that is both post-incrementable and post-decrementable.
This type:
class D
{
unsigned test;
};
does not have a publicly accessible member test. After all, I cannot write any of those statements:
D d;
d.test; // error
d.test++; // error
d.test--; // error
Hence, D
doesn't meet the concept test_is_available
. test
is very much not available.
If you want D
to be a test_is_available
, you'll need to make test
public
. You can't simply friend
something, that just breaks the concept system - you'll end up with a D
that's test_is_available
to some types but not others. That's not really how things are supposed to work.
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