This one's rather straightforward: according to the
So I figured alright, cool, I don't have to worry about redeclaring my concept for creating "printable enums"; I can declare the concept in both my logging header and my common types header, so I only have to include what's needed:
// common types header
#include <enum_traits.hpp>
template<typename E>
concept PrintableEnum = requires(E& e) { {enum_traits<E>::toString(e)} -> std::same_as<const char*>; };
template<PrintableEnum T>
std::ostream& operator<<(std::ostream& os, const T& o)
{
os << enum_traits<T>::toString(o);
return os;
}
// logging header
template<typename T>
concept PrintableEnum = requires(T& e) { {enum_traits<T>::toString(e)} -> std::same_as<const char*>; };
template<PrintableEnum T>
plog::Record& operator<<(plog::Record& rec, const T& o)
{
rec << enum_traits<T>::toString(o);
return rec;
}
(Ignore that the ostream overload could probably cover the plog record overload for the moment; that's probably how I'm gonna work around the problem, but I'm curious about this first.)
So when I try to compile this, MSVC throws a fit for every file both headers are included in (which one it points to as the error changes based on include order):
[build] commontypes.hpp(25): error C7571: 'PrintableEnum': variable template has already been initialized
[build] logging.hpp(162): note: see declaration of 'PrintableEnum'
Also note, doing what cppreference does in that screenshot and duplicating the PrintableEnum declaration right under the first one in one of the files is the same problem.
So, is cppreference wrong here? Does the standard not actually say anything about redeclarations? (I'm not actually sure where the official c standard is kept if not on cppreference...) Do I need to worry about redefinitions of concepts like anything else in C ?
Or is MSVC wrong here? (Not that I can do much about MSVC...) Searching the error message only got me stuff about other redeclarations, like classes and variables. Apparently "concept" is not very SEO-friendly.
CodePudding user response:
This one's rather straightforward: according to the cppreference article, concepts can be redeclared no problem:
No, it doesn't say that. This snippet:
template<Incrementable T>
void f(T) requires Decrementable<T>;
template<Incrementable T>
void f(T) requires Decrementable<T>; // OK, redeclaration
Doesn't redeclare a concept. It doesn't even declare a concept. What it's showing is declaring the same function template, twice. That function template is constrained (with both Incrementable
and Decrementable
), but it is still a function template.
You cannot redeclare a concept.
One reason is that you can't just declare a concept, you always also define it. You can't just write:
template <class T> concept C;
in the same way that you can write:
template <class T> void f();
f
here just a declaration of a function template, and I can add another declaration of it. But only one definition. This is ill-formed:
template <class T> void f() { }
template <class T> void f() { }
in the same way that this is ill-formed:
template <class T> concept C = true;
template <class T> concept C = true;
There can only be one definition.