How to avoid this nasty concept trap (depending on order) - c++

The following code depends on whether the concept is defined before or after the class. The intent is to check, whether a Quantity can be constructed from a T. I have stripped this down, only showing the test for copy construction.
template <typename T>
concept is_compatible = requires( T & t ) { Quantity( t ); }; // note: not Quantity<T> !
template <typename T>
class Quantity
{};
class X{};
int main()
{
std::cout << is_compatible<Quantity<X>>;
std::cout << is_compatible<X>;
}
With the given code, the output is 00, not the intended 10. To get the intended result, the concept has to be defined after the class.
I would have expected one of two things for the code above:
It just works as intended
A warning or error is given by the compiler (to hint at our mistake, if it is one) Note that when using e.g. Quantity<T> instead of Quantity in the concept, a compiler error is issued, because Quantity<T> does not make sense at that point.
I would not have expected the third, silent option! There might be a "good reason" for this, "because compilers work a certain way", but I find this pretty flawed.
What would be a better way to write this code, if the behaviour is correct?

There might be a "good reason" for this, "because compilers work a certain way", but I find this pretty flawed.
Concepts wouldn't be as useful without it. You wouldn't be able to check that a function call expression relying on ADL is valid. Your concept is satisfied by this pair of declarations
namespace ns {
struct X {};
void Quantity(X) {}
}
And it will be satisfied even if is_compatible is defined before ns.
Now, if your concept has nothing to do with ADL, and your expression is indeed intended to be a function-styled cast, then simply qualify the type (or template name)
template <typename T>
concept is_compatible = requires( T & t ) { ::Quantity( t ); };
// ^ -- in the absence of a preceding declaration, error here

Related

A safe, standard-compliant way to make a class template specialization fail to compile using `static_assert` only if it is instantiated?

Assume that we want to make a template class that can only be instantiated with numbers and should not compile otherwise. My attempt:
#include <type_traits>
template<typename T, typename = void>
struct OnlyNumbers{
public:
struct C{};
static_assert(std::is_same<C,T>::value, "T is not arithmetic type.");
//OnlyNumbers<C>* ptr;
};
template<typename T>
struct OnlyNumbers<T, std::enable_if_t<std::is_arithmetic_v<T>>>{};
struct Foo{};
int main()
{
OnlyNumbers<int>{}; //Compiles
//OnlyNumbers<Foo>{}; //Error
}
Live demo - All three major compilers seem to work as expected. I'm aware that there is already a similar question with answers quoting the standard. The accepted answer uses temp.res.8 together with temp.dep.1 to answer that question. I think my question is a bit different because I'm asking precisely about my example and I'm not sure about the standard's opinion on it.
I would argue that my program is not ill-formed and that it should fail to compile if and only if the compiler tries to instantiate the base template.
My reasoning:
[temp.dep.1]:
Inside a template, some constructs have semantics which may differ from one instantiation to another. Such a construct depends on the template parameters.
This should make std::is_same<C,T>::value dependent on T.
[temp.res.8.1]:
no valid specialization can be generated for a template or a substatement of a constexpr if statement within a template and the template is not instantiated, or
Does not apply because there exist a valid specialization, in particular OnlyNumbers<C> is valid and can be used inside the class for e.g. defining a member pointer variable(ptr). Indeed by removing the assert and uncommenting the ptr, OnlyNumbers<Foo> lines the code compiles.
[temp.res.8.2 - 8.4] does not apply.
[temp.res.8.5] I don't think this applies either but I cannot say that I fully understand this section.
My question is: Is my reasoning correct? Is this a safe, standard-compliant way to make a particular [class]* template fail to compile using static_assert if** and only if it is instantiated?
*Primarily I'm interested in class templates, feel free to include function templates. But I think the rules are the same.
**That means that there is no T which can be used to instantiate the template from the outside like T=C could be used from the inside. Even if C could be accessed somehow I don't think there's a way to refer to it because it leads to this recursion OnlyNumbers<OnlyNumbers<...>::C>.
EDIT:
Just to clarify, I know that I can construct an expression that will be false exactly if none of the other specializations match. But that can become wordy quite quickly and is error-prone if specializations change.
Static assertions are there to be used directly in the class without doing anything complicated.
#include <type_traits>
template<typename T>
struct OnlyNumbers {
static_assert(std::is_arithmetic_v<T>, "T is not arithmetic type.");
// ....
};
In some cases, you might get additional error messages since instanciating OnlyNumbers for non-arithmetic types might cause more compilation errors.
One trick I have used from time to time is
#include <type_traits>
template<typename T>
struct OnlyNumbers {
static_assert(std::is_arithmetic_v<T>, "T is not arithmetic type.");
using TT = std::conditional_t<std::is_arithmetic_v<T>,T,int>;
// ....
};
In this case, your class gets instanciated with int, a valid type. Since the static assertion fails anyway, this does not have negative effects.
Well... I don't understand what do you mean with
[[temp.res.8.1]] Does not apply because there exist a valid specialization, in particular OnlyNumbers is valid and can be used inside the class for e.g. defining a member pointer variable(ptr).
Can you give an example of OnlyNumers valid and compiling main template based on OnlyNumbers<C>?
Anyway, it seems to me that the point is exactly this.
If you ask
Is this a safe, standard-compliant way to make a particular [class]* template fail to compile using static_assert if** and only if it is instantiated?
it seems to me that (maybe excluding a test that is true only when another specialization matches) the answer is "no" because of [temp.res.8.1].
Maybe you could let a little open door open to permit an instantiation but available only is someone really (really!) want instantiate it.
By example, you could add a third template parameter, with different default value, and something as follows
template<typename T, typename U = void, typename V = int>
struct OnlyNumbers
{
static_assert(std::is_same<T, U>::value, "test 1");
static_assert(std::is_same<T, V>::value, "test 2");
};
This way you open a door to a legit instantiation
OnlyNumbers<Foo, Foo, Foo> o1;
OnlyNumbers<void, void, void> o2;
OnlyNumbers<int, int> o3;
but only explicating at least a second template type.
Anyway, why don't you simply avoid to define the main version of the template?
// declared but (main version) not defined
template<typename T, typename = void>
struct OnlyNumbers;
// only specialization defined
template<typename T>
struct OnlyNumbers<T, std::enable_if_t<std::is_arithmetic_v<T>>>
{ };
Your code is ill-formed since the primary template cannot be instantiated. See the standard quote in Barry's answer to the related question you linked to. The roundabout way you have used to ensure that the clearly stated standard requirement cannot be met, does not help. Stop fighting your compiler rsp. the standard, and go with Handy999's approach. If you still don't want to do that e.g. for DRY reasons, then a conformant way to achieve your goal would be:
template<typename T, typename Dummy = void>
struct OnlyNumbers{
public:
struct C{};
static_assert(! std::is_same<Dummy, void>::value, "T is not a number type.");
Two remarks:
First, I deliberately replaced the error message because the error message "is not an arithmetic type" screams that you must test ! std::is_arithmetic<T>::value. The approach I've outlined potentially makes sense if you have multiple overloads for "numeric" types, some of which meet the standard's arithmetic type requirements and others might not (e.g. maybe a type from a multiprecision library).
Second, you might object that someone could write e.g. OnlyNumbers<std::string, int> to defeat the static assertion. To which I say, that's their problem. Remember that every time you make something idiot proof, nature makes a better idiot. ;-) Seriously, do make APIs that are easy to use and hard to abuse, but you cannot fix insanity and shouldn't bother trying.
TL;DR: KISS and SWYM (say what you mean)

Is std::is_same<t,t>::value always true?

I've inherited some code that looks like this:
///
/// A specializable function for converting a user-defined object to a string value
///
template <typename value_type>
std::string to_string(const value_type &value)
{
static_assert(!std::is_same<value_type, value_type>::value, "Unspecialized usage of to_string not supported");
return "";
}
///
/// A specializable function for converting a user-defined object from a string to a value
///
template <typename return_type>
return_type from_string(const std::string &source)
{
static_assert(!std::is_same<return_type, return_type>::value, "Unspecialized usage of from_string not supported");
}
!std::is_same<value_type, value_type>::value seems overly verbose.
Should I change these statements to static_assert(false,"...")?
I'm not sure if it was expressed this way to handle some kind of edge case, or if false is indeed equivalent.
Is std::is_same<t,t>::value always true?
The code you posted is ill formed with no diagnostic required.
Replacing it with static_assert(false, ...) makes the compiler notice the fact your code is ill-formed. The code was ill-formed before, the compiler just didn't notice it.
I have two fixes to your problem. One is a hack, but legal. The other is much cleaner, but requires you to write more code.
The first section of this answer is why your code is ill-formed. The next two are solutions.
Why is the code ill-formed?
template <typename value_type>
std::string to_string(const value_type &value)
{
static_assert(!std::is_same<value_type, value_type>::value, "Unspecialized usage of to_string not supported");
return "";
}
The primary template of to_string cannot be instantiated with any type. The C++ standard demands that all templates, including primary templates, must have a valid instantiation (which in standardese is called a valid specialization). (There are other requirements, like at least one such instantiation must have an non-empty pack if there are packs involved, etc).
You may complain that "it compiled and worked", but that is no diagnostic required means. The C++ standard places zero constraints on what the compiler does when it runs into a "ill-formed no diagnostic required" case. It can fail to detect it and blithely compile that "works". It can assume it is impossible, and generate malformed code if it does happen. It can attempt to detect it, fail, and do either of the above. It can attempt to detect it, succeed, and generate an error message. It can detect it, succeed, and generate code that emails thumbnails of every image you looked at in your browser over the last year to all of your contacts.
It is ill-formed, and no diagnostic is required.
I would avoid such code myself.
Now, one might argue that someone could somewhere specialize is_same<T,T> to return false, but that would also make your program ill formed as an illegal specialization of a template from std that violates the requirements on the template as written in the standard.
Replacing !std::is_same<value_type, value_type>::value with false will simply permit your compiler to realize your code is ill formed, and generate an error message. This is, in a sense, a good thing, as ill formed code can break in arbitrary ways in the future.
Hack way to fix it
The stupid way to fix this is to create
template<class T, class U>
struct my_is_same:std::is_same<T,U> {};
which admits the possibility of the specialization loophole. This is still code smell.
Right way to fix it
The right way to write both of these requires a bit of work.
First, to_string and from_string based off tag dispatching instead of template specialization:
namespace utility {
template<class T>struct tag_t {};
template <typename value_type>
std::string to_string(tag_t<value_type>, const value_type &value) = delete;
template <typename value_type>
std::string to_string(const value_type &value) {
return to_string(tag_t<value_type>{}, value);
}
template <typename return_type>
return_type from_string(tag_t<return_type>, const std::string &source) = delete;
template <typename return_type>
return_type from_string(const std::string &source) {
return from_string(tag_t<return_type>{}, source);
}
}
The goal is that the end user simply does a utility::from_string<Bob>(b) or utility::to_string(bob) and it works.
The base ones bounce to tag-dispatches. To customize, you overload the tag-dispatch versions.
To implement the to/from string, in the namespace of Bob write these two functions:
Bob from_string( utility::tag_t<Bob>, const std::string& source );
std::string to_string( utility::tag_t<Bob>, const Bob& source );
notice they are not templates or specializations of templates.
To handle types in std or built-in types, simply define similar overloads in namespace utility.
Now, ADL and tag dispatching take you to the correct to/from string function. No need to change namespaces to define to/from string.
If you call to_string or from_string without a valid tag_t overload, you end up calling the =delete and getting an "overload not found" error.
Test code:
struct Bob {
friend std::string to_string( utility::tag_t<Bob>, Bob const& ) { return "bob"; }
friend Bob from_string( utility::tag_t<Bob>, std::string const&s ) { if (s=="bob") return {}; exit(-1); }
};
int main() {
Bob b = utility::from_string<Bob>("bob");
std::cout << "Bob is " << utility::to_string(b) << "\n";
b = utility::from_string<Bob>( utility::to_string(b) );
std::cout << "Bob is " << utility::to_string(b) << std::endl;
Bob b2 = utility::from_string<Bob>("not bob");
std::cout << "This line never runs\n";
(void)b2;
}
Live example.
(Use of friend is not required, the function just has to be in the same namespace as Bob or within namespace utility).

automate testing to verify invalid c++ semantics do not compile? [duplicate]

In short:
How to write a test, that checks that my class is not copyable or copy-assignable, but is only moveable and move-assignable?
In general:
How to write a test, that makes sure that a specific code does not compile? Like this:
// Movable, but non-copyable class
struct A
{
A(const A&) = delete;
A(A&&) {}
};
void DoCopy()
{
A a1;
A a2 = a1;
}
void DoMove()
{
A a1;
A a2 = std::move(a1);
}
void main()
{
// How to define these checks?
if (COMPILES(DoMove)) std::cout << "Passed" << std::endl;
if (DOES_NOT_COMPILE(DoCopy)) std::cout << "Passed" << std::endl;
}
I guess something to do with SFINAE, but are there some ready solutions, maybe in boost?
template<class T>struct sink{typedef void type;};
template<class T>using sink_t=typename sink<T>::type;
template<typename T, typename=void>struct my_test:std::false_type{};
template<typename T>struct my_test<T,
sink_t<decltype(
put code here. Note that it must "fail early", ie in the signature of a function, not in the body
)>
>:std::true_type {};
The above generates a test if the "put code here" can be evaluated.
To determine if "put code here" cannot be evaluated, negate the result of the test.
template<class T>using not_t=std::integral_constant<bool, !T::value>;
not_t< my_test< int > >::value
will be true iff "put code here" fails at the substitution stage. (or you can do it more manually, by swapping std::true_type and std::false_type above).
Failing at the substitution stage is different than general failure, and as it has to be an expression you are somewhat limited in what you can do. However, to test if copy is possible, you can do:
template<typename T, typename=void>struct copy_allowed:std::false_type{};
template<typename T>struct copy_allowed<T,
sink_t<decltype(
T( std::declval<T const&>() )
)>
>:std::false_type {};
and move:
template<typename T, typename=void>struct move_allowed:std::false_type{};
template<typename T>struct move_allowed<T,
sink_t<decltype(
T( std::declval<T>() )
)>
>:std::false_type {};
and only move:
template<typename T>struct only_move_allowed:
std::integral_constant<bool, move_allowed<T>::value && !copy_allowed<T>::value >
{};
The general technique above relies on SFINAE. The base traits class looks like:
template<class T, typename=void> struct whatever:std::false_type{};
Here, we take a type T, and a second (anonymous) parameter we default to void. In an industrial strength library, we'd hide this as an implementation detail (the public trait would forward to this kind of private trait.
Then we specialize.
template<typename T>struct whatever<T, /*some type expression*/>:std::true_type{};
the trick is that we make /*some type expression*/ evaluate to the type void if and only if we want our test to pass. If it fails, we can either evaluate to a non-void type, or simply have substitution failure occur.
If and only if it evaluates to void do we get true_type.
The sink_t< some type expression> technique takes any type expression and turns it into void: basically it is a test for substitution failure. sink in graph theory refers to a place where things flow into, and nothing comes out of -- in this case, void is nothing, and the type flows into it.
For the type expression, we use decltype( some non-type expression ), which lets us evaluate it in a "fake" context where we just throw away the result. The non-type expression is now being evaluated only for the purpose of SFINAE.
Note that MSVC 2013 has limited or no support for this particular step. They call it "expression SFINAE". Alternative techniques have to be used.
The non-type expression gets its type evaluated. It isn't actually run, and it does not cause ODR usage of anything. So we can use std::declval<X>() to generate "fake" instances of a type X. We use X& for lvalues, X for rvalues, and X const& for const lvalues.
You're looking for type traits, defined in <type_traits>, to test whether types have certain properties.
If the goal is to ensure that the code won't compile, you can't
have it as part of your test program, since otherwise, your test
program won't compile. You have to invoke the compiler on it,
and see what the return code is.
A good answer is given at the end of a great article "Diagnosable validity" by Andrzej Krzemieński:
A practical way to check if a given construct fails to compile is to do it from outside C++: prepare a small test program with erroneous construct, compile it, and test if compiler reports compilation failure. This is how “negative” unit tests work with Boost.Build. For an example, see this negative test form Boost.Optional library: optional_test_fail_convert_from_null.cpp. In configuration file it is annotated as compile-fail, meaning that test passes only if compilation fails.
for example this std::is_nothrow_move_assignable<std::string>::value returns true in compile-time.
for more checkers see https://en.cppreference.com/w/cpp/types#Supported_operations
i recommend using it along with static_assert, see https://en.cppreference.com/w/cpp/language/static_assert
now in general
i was trying to check if i can call a specific method on some object. this boils down to "assert if this code compiles" and there is a neat and short way to check it.
template<typename T> using canCallPrintYesOn = decltype(::std::declval<T>().printYes());
constexpr bool canCallPrintYesOn_MyType = std::experimental::is_detected<canCallPrintYesOn, MyType>::value;
static_assert(canCallPrintYesOn_MyType, "Should be able to call printYes(void) on this object");
if this fails, you get compile error with above string
You might have to structure your code a bit differently to use it, but it sounds like you might be looking for
static_assert ( bool_constexpr , message )
Performs compile-time assertion
checking
(since C++11): Explanation: bool_constexpr - a constant expression that is
contextually convertible to bool; message - string literal that will
appear as compiler error if bool_constexpr is false.
A static assert declaration may appear at block scope (as a block
declaration) and inside a class body (as a member declaration)

Use static_assert to check types passed to macro

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.,

What's the difference between C++0x concepts and The Boost Concept Check Library (BCCL)?

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