I needed to specify a concept for Messages - very simple as long as it has a typeTag static member - see below, I simply write - and I do not think I am supposed to write much more than this if concept is easy to use:
template <typename M>
concept MessageC = requires(M m) {M::typeTag;};
then I have trouble for a straightforward use:
template <MessageC Message>
void send(Message&& m){...}
The concept turns out to be not usable when Message is deduced to be Finish&, where Finish is actually a Message type. Finish&::typeTag is ill-formed as the g++ 9 compiler complains.
I could do things like remove_reference in the concept definition except I do not know that is the recommended way. Is C++ Concept meant to be simpler to use than that?
Thanks.
Your concept is best written this way:
template<typename M>
concept MessageC = requires { typename M::typeTag; };
i.e. you need typename for the dependent name, and there is no need for arguments in the requires expression.
When it comes to your actual question:
template<MessageC Message>
void send(Message&&) {}
is equivalent to:
template<typename Message>
void send(Message&&) requires MessageC<Message> {}
but, as you noted, Message is a forwarding reference, which can collapse to either lvalue or rvalue references.
Therefore, your function should read:
template<typename Message>
void send(Message&&) requires MessageC<std::remove_reference_t<Message>> {}
consistently with the language rules.
I could do things like remove_reference in the concept definition except I do not know that is the recommended way.
The standard does it plenty of times. It doesn't usually do this specifically for member typedefs and such, but that's because the standard typically uses traits classes to get "members" of a type, so that fundamental types could be used in those cases. Pointers don't have a value_type method, so the concepts library instead uses iterator/readable_traits.
Concepts are still C++; you can't just ignore rules of the language. Statements in concepts still must follow the rules of C++. A concept's template parameters work just like any other template parameters.
I end up writing the following to limit the typeTag static value within 2 bytes
template <typename M>
concept MessageC = std::remove_reference<M>::type::typeTag >= 0x0
&& std::remove_reference<M>::type::typeTag <= 0xffffu;
Related
I am want a static check of the parameter type of lambdas. I've written this code below and it seems to produce the correct result.
struct B { };
auto lamBc = [](B const& b) { std::cout << "lambda B const" << std::endl; };
template<typename ClosureType, typename R, typename Arg>
constexpr auto ArgType(R (ClosureType::*)(Arg) const)->Arg;
template <typename T>
using ArgType_t = decltype(ArgType(&T::operator()));
// ArgType_t<lamBc> is "reference to B const"
However, I noticed that, for example, the standard library uses class template specialization to extract the referred-to type from a reference type in std::remove_reference. So I tried that approach and it also seems to produce the correct result.
template<typename L>
struct ArgType2;
template<typename ClosureType, typename R, typename Arg>
struct ArgType2<R (ClosureType::*)(Arg) const>
{
typedef Arg type;
};
template <typename T>
using ArgType2_t = typename ArgType2<decltype(&T::operator())>::type;
// ArgType2_t<lamBc> is also "reference to B const"
My questions are: Which is the standard way to extract types from a pattern expression? What are the trade-offs in either approach?
Both your approaches are valid, interchangeable and lead to the same result (deduce the type of the parameter lambda accepts).
For type traits it is required by the standard (see 23.15.1 Requirements) that:
A UnaryTypeTrait describes a property of a type. It shall be a class template that takes one template type argument and, optionally, additional arguments that help define the property being described. ...
A BinaryTypeTrait describes a relationship between two types. It shall be a class template that takes two template type arguments
and, optionally, additional arguments that help define the
relationship being described. ...
A TransformationTrait modifies a property of a type. It shall be a class template that takes one template type argument and, optionally, additional arguments that help define the modification. ...
I suppose that this requirement appeared mostly for historical reasons as decltype functionality was introduced after type traits had been proposed (and these type traits were based on type traits from boost which had been created even earlier, see, for example, this).
Also, note, that class templates are more flexible for general purpose type traits than the logic based on functions declarations and decltype.
The main point is that with C++11 and later in your particular case you are free to use the way which is the most convenient and reflects the programming logic better.
They are both "standard", the decltype() way is simply available in modern C++ variants, the PTS method was the only way to do this sort of things under the original C++ standard.
Similar question: Why are type_traits implemented with specialized template structs instead of constexpr? – but with a different answer.
I realise that alias templates cannot be specialised and hence can’t currently be used to implement type traits directly1. However, this is a conscious decision of the committee, and as far as I see there is no technical reason to forbid this.
So wouldn’t it have made more sense to implement type traits as alias templates, streamlining their syntax?
Consider
typename enable_if<is_pointer<T>::value, size_t>::type
address(T p);
versus
enable_if<is_pointer<T>, size_t> address(T p);
Of course, this introduces a breaking interface change when moving from Boost.TypeTraits – but is this really such a big problem?
After all, the code will need to be modified anyway since the types reside in different namespace and, as many modern C++ programmers are reluctant to open namespaces, will be qualified explicitly (if it would be changed at all).
On the other hand, it vastly simplifies the code. And given that template metaprogramming often gets deeply nested, convoluted and complex, it seems obvious that a clearer interface is beneficial.
Am I missing something? If not, I’d appreciate an answer that is not mere guesswork but relies on (and can cite) knowledge of the committee’s decision rationale.
1 But very well indirectly! Consider:
template <typename T> using is_pointer = typename meta::is_pointer<T>::type;
Where meta::is_pointer<T> corresponds to the current std::is_pointer<T> type.
The most concrete answer to your question is: No one ever proposed doing it that way.
The C++ standards committee is a multi-national, multi-corporation collection of volunteers. You're thinking of it as a design committee within a single organization. The C++ standards committee literally can not do anything without a proposal to put words into the draft standard.
I imagine that the reason there was no proposal is that type traits was an early proposal, with the boost implementation dating back to around 2000. And template aliases were late in getting implemented. Many of the committee members are reluctant to propose something that they have not implemented. And there was simply little opportunity to implement your proposal.
There was a lot of pressure to ship C++11. It really was intended to ship in 2009 and when that ship date slipped, it was very tough to do anything to the working paper besides fix the features already under consideration. At some point you've got to put great new ideas on the back burner, lest you will never ship.
Update
As of C++14, the TransformationTraits (those which result in a type) now have template alias spellings, for example:
template <bool b, class T = void>
using enable_if_t = typename enable_if<b,T>::type;
And the C++1z working draft now has template variable spellings for the traits resulting in values:
template <class T>
constexpr bool is_pointer_v = is_pointer<T>::value;
Also, even in C++11 one could do:
typename enable_if<is_pointer<T>{}, size_t>::type
address(T p);
I.e. you can use {} in place of ::value (assuming your compiler has constexpr support). In C++14 that becomes:
enable_if_t<is_pointer<T>{}, size_t>
address(T p);
And in C++1z:
enable_if_t<is_pointer_v<T>, size_t>
address(T p);
Note that the difference between C++1z and C++14 is so minimal that it doesn't even save characters, just changes {} to _v and changes where you put these two characters.
Type traits, like several other libraries including <memory> and <functional>, were inherited from C++ TR1. Although that was a less formal document, it was more formal than Boost, and compatibility is worthwhile.
Also, note that type traits are all derived from std::integral_constant<bool>, which does implement a constexpr conversion function to bool. So that at least saves the ::value parts, if you so choose.
As a complete side-note since there seems to be confusion on how aliases may or may not help a trait like std::is_pointer:
You can go the Boost.MPL route and decide that you'll use Boost.MPL-style integral constants, which means types
template<typename Cond, typename Then = void>
using enable_if = typename std::enable_if<Cond::value, Then>::type;
// usage:
template<
typename T
, typename = enable_if<std::is_pointer<T>>
>
size_t address(T p);
or you can decide to use values instead
template<bool Cond, typename Then>
using enable_if = typename std::enable_if<Cond, Then>::type;
// can use ::value
template<
typename T
, typename = enable_if<std::is_pointer<T>::value>>
>
size_t address(T p);
// or constexpr conversion operator
template<
typename T
, typename = enable_if<std::is_pointer<T> {}>
>
size_t address(T p);
Note that in the latter case it's not possible to use enable_if<std::is_pointer<T>()>: std::is_pointer<T>() is a function type (taking void and returning std::is_pointer<T>) and is invalid since our alias takes a value and not a type in this case. The braces ensure that it is a constant expression instead.
As you may have noticed, std::is_pointer doesn't benefit from template aliases at all. This isn't surprising at it's a trait where the interesting part is accessing ::value, not ::type: template aliases can only help with member types. The type member of std::is_pointer isn't interesting since it's an Boost.MPL-style integral constant (in this case either std::true_type or std::false_type), so this doesn't help us. Sorry!
Is there any reason why the standard specifies them as template structs instead of simple boolean constexpr?
In an additional question that will probably be answered in a good answer to the main question, how would one do enable_if stuff with the non-struct versions?
One reason is that constexpr functions can't provide a nested type member, which is useful in some meta-programming situations.
To make it clear, I'm not talking only of transformation traits (like make_unsigned) that produce types and obviously can't be made constexpr functions. All type traits provide such a nested type member, even unary type traits and binary type traits. For example is_void<int>::type is false_type.
Of course, this could be worked around with std::integral_constant<bool, the_constexpr_function_version_of_some_trait<T>()>, but it wouldn't be as practical.
In any case, if you really want function-like syntax, that is already possible. You can just use the traits constructor and take advantage of the constexpr implicit conversion in integral_constant:
static_assert(std::is_void<void>(), "void is void; who would have thunk?");
For transformation traits you can use a template alias to obtain something close to that syntax:
template <bool Condition, typename T = void>
using enable_if = typename std::enable_if<Condition, T>::type;
// usage:
// template <typename T> enable_if<is_void<T>(), int> f();
//
// make_unsigned<T> x;
Note: this ends up looking more like a rant than a proper answer... I did got some itch reading the previous answers though, so please excuse me ;)
First, class traits are historically done with template structures because they predate constexpr and decltype. Without those two, it was a bit more work to use functions, though the various library implementations of is_base_of had to use functions internally to get the inheritance right.
What are the advantages of using functions ?
inheritance just works.
syntax can be more natural (typename ::type looks stupid TM)
a good number of traits are now obsolete
Actually, inheritance is probably the main point against class traits. It's annoying as hell that you need to specialize all your derived classes to do like momma. Very annoying. With functions you just inherit the traits, and can specialize if you want to.
What are the disadvantages ?
packaging! A struct trait may embed several types/constants at once.
Of course, one could argue that this is actually annoying: specializing iterator_traits, you just so often gratuitously inherit from std::iterator_traits just to get the default. Different functions would provide this just naturally.
Could it work ?
Well, in a word where everything would be constexpr based, except from enable_if (but then, it's not a trait), you would be going:
template <typename T>
typename enable_if<std::is_integral(T()) and
std::is_signed(T())>::type
Note: I did not use std::declval here because it requires an unevaluated context (ie, sizeof or decltype mostly). So one additional requirement (not immediately visible) is that T is default constructible.
If you really want, there is a hack:
#define VALUE_OF(Type_) decltype(std::declval<T>())
template <typename T>
typename enable_if<std::is_integral(VALUE_OF(T)) and
std::is_signed(VALUE_OF(T))>::type
And what if I need a type, not a constant ?
decltype(common_type(std::declval<T>(), std::declval<U>()))
I don't see a problem either (and yes, here I use declval). But... passing types has nothing to do with constexpr; constexpr functions are useful when they return values that you are interested in. Functions that return complex types can be used, of course, but they are not constexpr and you don't use the value of the type.
And what if I need to chain trais and types ?
Ironically, this is where functions shine :)
// class version
template <typename Container>
struct iterator { typedef typename Container::iterator type; };
template <typename Container>
struct iterator<Container const> {
typedef typename Container::const_iterator type;
};
template <typename Container>
struct pointer_type {
typedef typename iterator<Container>::type::pointer_type type;
};
template <typename Container>
typename pointer_type<Container>::type front(Container& c);
// Here, have a cookie and a glass of milk for reading so far, good boy!
// Don't worry, the worse is behind you.
// function version
template <typename Container>
auto front(Container& c) -> decltype(*begin(c));
What! Cheater! There is no trait defined!
Hum... actually, that's the point. With decltype, a good number of traits have just become redundant.
DRY!
Inheritance just works!
Take a basic class hierarchy:
struct Base {};
struct Derived: Base {};
struct Rederived: Derived {};
And define a trait:
// class version
template <typename T>
struct some_trait: std::false_type {};
template <>
struct some_trait<Base>: std::true_type {};
template <>
struct some_trait<Derived>: some_trait<Base> {}; // to inherit behavior
template <>
struct some_trait<Rederived>: some_trait<Derived> {};
Note: it is intended that the trait for Derived does not state directly true or false but instead take the behavior from its ancestor. This way if the ancestor changes stance, the whole hierarchy follows automatically. Most of the times since the base functionality is provided by the ancestor, it makes sense to follow its trait. Even more so for type traits.
// function version
constexpr bool some_trait(...) { return false; }
constexpr bool some_trait(Base const&) { return true; }
Note: The use of ellipsis is intentional, this is the catch-all overload. A template function would be a better match than the other overloads (no conversion required), whereas the ellipsis is always the worst match guaranteeing it picks up only those for which no other overload is suitable.
I suppose it's unnecessary to precise how more concise the latter approach is ? Not only do you get rid of the template <> clutter, you also get inheritance for free.
Can enable_if be implemented so ?
I don't think so, unfortunately, but as I already said: this is not a trait. And the std version works nicely with constexpr because it uses a bool argument, not a type :)
So Why ?
Well, the only technical reason is that a good portion of the code already relies on a number of traits that was historically provided as types (std::numeric_limit) so consistency would dictate it.
Furthermore it makes migration from boost::is_* just so easier!
I do, personally, think it is unfortunate. But I am probably much more eager to review the existing code I wrote than the average corporation.
One reason is that the type_traits proposal is older than the constexpr proposal.
Another one is that you are allowed to add specializations for your own types, if needed.
Probably because boost already had a version of type_traits that was implemented with templates.
And we all know how much people on the standards committee copy boost.
I would say the mainreason is that type_traits was already part of tr1 and was therefore basically guaranteed to end up in the standard in more or less the same form, so it predates constexpr. Other possible reasons are:
Having the traits as types allows for overloading functions on the type of the trait
Many traits (like remove_pointer) define a type instead of a value, so they have to be expressed in this way. Having different interfaces for traits defining values and traits defining types seems unnessecary
templated structs can be partial specialized, while functions can't, so that might make the implementation of some traits easier
For your second question: As enable_if defines a type (or not, if it is passed false) a nested typedef inside a struct is really the way to go
I unfortunately have several macros left over from the original version of my library that employed some pretty crazy C. In particular, I have a series of macros that expect certain types to be passed to them. Is it possible to do something along the lines of:
static_assert(decltype(retval) == bool);
And how? Are there any clever alternatives?
Yes I'm aware macros are bad. I'm aware C++ is not C, etc.
Update0
Here is some related code, and the source file. Suggestions are welcome. The original question remains the same.
I found this to be the cleanest, using #UncleBens suggestion:
#include <type_traits>
static_assert(std::is_same<decltype(retval), bool>::value, "retval must be bool");
Disclaimer: This is a bad answer, there are definitely far better solutions. Just an example :)
It is bound to be already implemented, but it's trivial to implement yourself;
template <class T1, class T2> struct CheckSameType; //no definition
template <class T> struct CheckSameType<T,T>{}; //
template <class T1, class T2>
AssertHasType(T2)
{
CheckSameType<T1, T2> tmp; //will result in error if T1 is not T2
}
To be used like this:
AssertHasType<bool>(retval);
Alternative (suggested by GMan):
template <class T1, class T2> struct SameType
{
enum{value = false};
}
template <class T> struct SameType<T,T>
{
enum{value = true};
};
To be used like
static_assert(SameType<decltype(retval), bool>::value);
It appears you need decltype because you've got an expression, but want to verify a type. There are already enough ways to do that now (C++03). For instance, to check a bool
inline void mustBeBool(bool) { }
template<typename T> inline void mustBeBool(T t) { & (&t); } // Takes address of rvalue (&t)
// Use:
#define DifficultMacro(B) do { mustBeBool(B); foo(B); } while (false)
If you DO care about the const and volatile qualifiers, and want to ensure the const and volatile parts of the types also exactly match the type you are comparing against, do like #Matt Joiner says:
#include <type_traits>
static_assert(std::is_same<decltype(my_variable), uint64_t>::value,
"type must be `uint64_t`");
I you do NOT care about const, however, and want to simply ensure the type is a certain type without regard for const, do the following instead. Note that std::remove_const<>::type is required here:
static_assert(std::is_same<std::remove_const<decltype(my_variable)>::type, uint64_t>::value,
"type must be `uint64_t` OR `const uint64_t`");
The same goes for volatile. In case you don't care about the volatile part of the type either, you can ignore it with std::remove_volatile<>::type:
static_assert(std::is_same<std::remove_volatile<decltype(my_variable)>::type, uint64_t>::value,
"type must be `uint64_t` OR `volatile uint64_t`");
If you don't care about const OR volatile, you can remove them both with std::remove_cv<>::type:
static_assert(std::is_same<std::remove_cv<decltype(my_variable)>::type, uint64_t>::value,
"type must be `uint64_t` OR `const uint64_t` OR `volatile uint64_t` OR `volatile const uint64_t`");
Note also that as of C++17 you can do:
std::remove_cv_t<decltype(my_variable)> in place of std::remove_cv<decltype(my_variable)>::type, and:
std::is_same_v<some_type, another_type> in place of std::is_same<some_type, another_type>::value.
References:
Use static_assert to check types passed to macro
https://en.cppreference.com/w/cpp/types/is_same
https://en.cppreference.com/w/cpp/language/decltype
https://en.cppreference.com/w/cpp/header/type_traits
https://en.cppreference.com/w/cpp/types/remove_cv - std::remove_cv<>, std::remove_const<>, std::remove_volatile<>
Related:
[another answer of mine where I use the above static_assert tricks] How to make span of spans
Static assert in C [my own answer]
How to use static assert in C to check the types of parameters passed to a macro [my own question]
Typechecking macro arguments in C
*****C++ Limit template type to numbers
Most macros can be replaced with inline functions and/or templates. As a case in point, the overly clever argument-size-checking Posix isnan macro is a template in C++0x. Oh,bad example, but you get the idea.
The main exceptions to that rule are macros that essentially implement higher level language features. For example, smarter exception handling, or covariance, or a parameterized set of declarations.
In some cases the macros that can't be reasonable expressed as inline functions or templates, can be replaced with a smarter kind of preprocessing, namely code generation. Then you have a script somewhere that generates the necessary code. For example, it's possible to do options classes in pure C++ with macros and templates, but it's hairy, and as an easier-to-grok and perhaps more maintainable alternative one might use a script that generates the requisite classes, at the cost of extra build steps and dealing with multiple languages.
Cheers & hth.,
Concepts didn't make the C++0x standard, but Boost still provides The Boost Concept Check Library (BCCL). I guess that BCCL doesn't cover everything that was meant to into the C++0x standard. What is the difference between BCCL and the proposed C++0x solution?
Checking the template definition
A big difference of concepts to these manual solutions is that concepts allow the definition of a template to be type-checked without doing anything special. The concept check library allows only the *use* of it to be type checked (unless you manually write test-instantiation types or make use of provided types for standard cases, see below). Example:
template<typename InputIterator>
int distance(InputIterator a, InputIterator b)
{ return b - a; }
You may now sprinkle that template with concept checks and traits, but you will never get an error after writing that template - because the Standard allows the compiler to delay compiling the template until instantiation. For checking, you have to write "archetype" classes, which contain exactly those operations that are required by the interface and then instantiate them artificially.
Reading the documentation of BCCL, i found it already includes the common archetypes like "default constructible". But if you write your own concepts, you will have to also provide your own archetypes, which isn't easy (you have to find exactly the minimal functionality a type has to provide). For example, if your archetype contains a operator-, then the test of your template with that (incorrect) archetype will succeed, although the concepts don't require such an operator.
The rejected concepts proposal creates archetypes for you automatically, based on the requirements that were specified and that were implied (a pointer type T* used in a parameter will imply the PointeeType requirement for T, for example). You don't have to care about this stuff - except of course when your template definition contains a type error.
Checking semantic requirements
Consider this code, using hypothetical concept checks
template<ForwardIterator I>
void f(I a, I b) {
// loop two times!
loopOverAToB(a, b);
loopOverAToB(a, b);
}
The BCCL manual says that semantic requirements are not checked. Only syntax requirement and types are checked. Consider a forward iterator: There exists the semantic requirement that you may use it in multi-pass algorithms. Syntax-checking only won't be able to test this requirement (think about what happens if a stream iterator accidentally would pass that check!)
In the rejected proposal, you had to explicitly put auto in front of concept definitions to make the compiler flag success after syntax-checking. If auto wasn't specified, then a type explicitly had to define a concept map to say it supports that concept. A stream iterator would thus never be taken to pass a ForwardIterator check.
Syntax remapping
This was another feature. A template such as
template<InputIterator I>
requires OutputStreamable<I::value_type>
void f(I a, I b) {
while(a != b) std::cout << *a++ << " ";
}
Can be used like the following, if the user would provide a concept map that teaches the compiler how to dereference an integer, and thus how an integer satisfies the InputIterator concept.
f(1, 10);
This is the benefit of a language-based solution, and cannot be solved by BCCL ever, i believe.
Concept based Overloading
On a quick read of BCCL, i can also not spot anything that allows this to happen. A concept matching failure seems to cause a hard compilation error. The rejected proposal allows the following:
template<ForwardIterator I>
I::difference_type distance(I a, I b) {
I::difference_type d = 0; while(a != b) ++a, ++d;
return d;
}
template<RandomAccessIterator I>
I::difference_type distance(I a, I b) {
return b - a;
}
If a type could be used with both templates, then the second template would be used, because it's more specialized: RandomAccessIterator refines the ForwardIterator concept.
The C++0x concept feature would be a core language feature, whose whole process would be done by the compiler.
The Boost Concept Check Library is the almost same feature but written in C++ and macro as a library to simulate some of the feature. It cannot do all that would be required in the final language feature (depending on the final feature definition) but provide some equivalent solutions for template type checking (and other compile time checks).
As suggested, as the C++0x concept is a language features, it would allow to provide more elegant semantic and allow compiler to use informations not currently available to the program, allowing more detailed or intelligent errors at compile time (as the concept first purpose is to allow abstract type check in templates).
Disclaimer: I wasn't able to successfully use BCCL within the last 30 minutes even though I had Boost installed already. The example you see below looks OK according to the BCCL documentation of Boost 1.37 but didn't work. I guess this counts as disadvantage.
With BCCL you only get something like static assertions whereas the core language concept feature provides full modular type checking and is able to prevent some function template from participating in overload resolution. With native concepts the body of a constrained template can be checked immediately by the compiler whereas BCCL doesn't make the compiler check anything in that respect. You have to manually instantiate your template with "arche type" parameters to see if the template uses any operations that are not available (for example, operator-- on forward iterators).
As for overload resolution here's an example:
template<typename Iter>
void foo(Iter,Iter) {
BOOST_CONCEPT_ASSERT((RandomAccessIterator<Iter>));
}
void foo(long, int);
int main() {
foo(2,3); // compile-time error
}
The template is a better match because the non-template requires a conversion from int to long. But instantiation fails because int is not an iterator. You get a nice error message explaining it but that's not very satisfying, is it? With native concepts you could have written
template<typename Iter>
requires RandomAccessIterator<Iter>
void foo(Iter,Iter) {}
void foo(long, int);
int main() {
foo(2,3); // OK, picks non-template foo
}
Here, the function template will not take part in overload resolution because the requirement for T=int is not satisfied. Great!
We still get to constrain function templates with the SFINAE trick. C++0x extends SFINAE to expressions and together with decltype and default template arguments for function templates we can write
template<typename T> T&& make();
template<typename Iter, class = decltype( *make<Iter>() )>
void foo(Iter,Iter) {}
void foo(long, int);
int main() {
foo(2,3); // OK, picks non-template foo
}
In this case, template argument deduction will fail silently because the compiler doesn't know what the type of the expression ∗make<Iter>() is supposed to be (we can't dereference an int). With a bit of template metaprogramming and some macros we can get pretty close to imposing arbitrary structural constraints on template parameters in a readable way. Here's what it might look like assuming appropriate definitions for REQUIRES and RandomAccessIterator:
template <typename Iter
REQUIRES( RandomAccessIterator<Iter> )
>
void foo(Iter,Iter) {}
HTH,
S