Make C++ fail compilation on specific instantiation of template function - c++

I'm working on a project which has an template function as so:
template <class T>
T foo<T>(T val) { return someFunc(val); }
template <>
bool foo<bool>(bool val) { return otherFunc(val); };
Now, I have a class Bar, which I don't want to accept as input. In fact, I want it to generate an easy to spot compile error. The problem is that if I do this:
template <>
Bar foo<Bar>(Bar val) { static_assert(false,"uh oh..."); }
It fails on every compile. I found https://stackoverflow.com/a/3926854/7673414, which says that I need to make reference to the template type, otherwise the static assert always takes place. The problem is I don't have a template type here. If I do:
template< typename T >
struct always_false {
enum { value = false };
};
template <>
Bar foo<Bar>(Bar val) { static_assert(always_false<Bar>::value,"uh oh..."); }
then it also always fails compiling. Is there a way to ensure that an instantiation of the template with type Bar always causes a compile error?

Since foo is a complete specialization, it will always get compiled, and the static assert will always get called.
However, there’s an easier way:
template <>
Bar foo<Bar>(Bar val) = delete;
This will say that this specific version is deleted, and cannot be called.

Another way is enable the template (not specialized version) only if the type is different from Bar
template <class T>
typename std::enable_if< ! std::is_same<T, Bar>::value, T>::type foo<T>(T val)
{ return someFunc(val); }
If you can use C++14, is can be simplified using std::enable_if_t
template <class T>
std::enable_if_t< ! std::is_same<T, Bar>::value, T> foo<T>(T val)
{ return someFunc(val); }

You can use std::is_same to help with your requirement.
template <class T>
T foo<T>(T val)
{
// Make sure T is not Bar
static_assert(std::is_same<T, Bar>::value == false, "uh oh...");
return someFunc(val);
}

If you are using c++17, you can put everything together with constexpr if:
template< typename T >
auto foo( T val )
{
static_assert( !std::is_same_v<T,Bar> );
if constexpr( std::is_same_v<T,bool> )
{
return other_func( val );
}
else
{
return some_func( val );
}
}
Then you can static_assert at the first line, without the pain of compilation failure of the template specific instantiation.
A live example is available at https://wandbox.org/permlink/PpR6G0gcvMRoxhhZ

Related

C++20 requires expression does not catch static_assert

I was really excited when I first heard about C++20 constraints and concepts, and so far I've been having a lot of fun testing them out. Recently, I wanted to see if it's possible to use C++20 concepts to test the constraints of classes or functions. For example:
template <int N>
requires (N > 0)
class MyArray { ... };
template <int N>
concept my_array_compiles = requires {
typename MyArray<N>;
};
my_array_compiles<1>; // true
my_array_compiles<0>; // false
At first I didn't have any issues, but I encountered a case where static_assert in a dependent function prevents compilation, even though it appears in a requires expression. Here is an example that illustrates this:
template <bool b>
requires b
struct TestA {
void foo() {}
};
template <bool b>
struct TestB {
static_assert(b);
void foo() {}
};
template <template<bool> class T, bool b>
concept can_foo = requires (T<b> test) {
test.foo();
};
can_foo<TestA, true>; // true
can_foo<TestA, false>; // false
can_foo<TestB, true>; // true
// can_foo<TestB, false>; does not compile
TestA and TestB should work similarly for most use cases (although I found that TestB<false> can be used as a type as long as it isn't instantiated or dereferenced). However, my expectation was that a failed static_assert within a requires expression would cause it to evaluate to false instead. This is especially important for using library code that still uses static_assert. For example, std::tuple_element:
template <class T>
concept has_element_0 = requires {
typename tuple_element_t<0, T>;
};
has_element_0<tuple<int>>; // true
// has_element_0<tuple<>>; does not compile
When I pass in an empty tuple to the above concept, I get the error static_assert failed due to requirement '0UL < sizeof...(_Types)' "tuple_element index out of range". I've tested this on g++ 10.3.0 and clang 12.0.5. I was able to work around this issue by providing a wrapper that uses constraints, but it somewhat defeats the purpose since I am essentially preventing the compiler from seeing the static_assert by enforcing the same condition at a higher level.
template <size_t I, class T>
requires (I >= 0) && (I < tuple_size_v<T>)
using Type = tuple_element_t<I, T>;
template <class T>
concept has_element_0 = requires {
typename Type<0, T>;
};
has_element_0<tuple<int>>; // true
has_element_0<tuple<>>; // false
And it doesn't always work depending on how std::tuple_element is used:
template <size_t I, class T>
requires (I >= 0) && (I < tuple_size_v<T>)
tuple_element_t<I, T> myGet(const T& tup) {
return get<I>(tup);
}
template <class T>
concept has_element_0 = requires (T tup) {
myGet<0>(tup);
};
has_element_0<tuple<int>>; // true
// has_element_0<tuple<>>; does not compile
So ultimately my questions are: is this expected behavior that requires expressions don't take static_assert into account? If so, what was the reason for that design? And finally, is there a better way to accomplish my goal on classes with static_assert without using the above workaround?
Thanks for reading.
Yes, nothing in the content of the stuff you interact with is checked. Just the immediate context of the declaration.
In some cases with decltype the non immediate context of some constructs is checked, but any errors remain hard.
This was done (way back) to reduce the requirements on compilers. Only in what is known as "immediate context" do the compilers need to be able to cleanly back out when they see an error and continue compiling.
Static assert is never suitable for this purpose. Static assert, if hit, ends the compilation.
If you want to avoid the static assert (that is expected to end compilation) then you need to provide an alternative.
Once the concept is designed, create a variant for the not (!) of that concept:
#include <tuple>
#include <variant>
template <std::size_t I, class T>
requires (I >= 0) && (I < std::tuple_size_v<T>)
using Type = std::tuple_element_t<I, T>;
template <class T>
concept has_element_0 = requires {
typename Type<0, T>;
};
bool test1()
{
return has_element_0<std::tuple<int>>; // true
}
bool test2()
{
return has_element_0<std::tuple<>>; // false
}
template <std::size_t I, class T>
requires (I >= 0) && (I < std::tuple_size_v<T>)
std::tuple_element_t<I, T> myGet_impl(const T& tup) {
return get<I>(tup);
}
template <class T>
concept alt_has_element_0 = requires (T tup) {
myGet_impl<0>(tup);
};
template <class T>
auto myGet0();
template <class T>
requires (alt_has_element_0<T>)
auto myGet0(const T& tup)
{
return myGet_impl<0, T>(tup);
}
auto test3()
{
std::tuple<int> X{7};
return myGet0(X); // true
}
template <class T>
requires (!alt_has_element_0<T>)
auto myGet0(const T& tup)
{
return std::monostate{};
}
auto test4()
{
std::tuple<> X;
return myGet0(X); // true
}
see it here;
Notice for test4() to compile, the code above defiles what to do if we do not fulfill the requirements of the concept. I stole std::monostate from variant for this.

template parameters in templated type function

The following code does not compile with gcc 6.4 in Debian Sid, due to the error: "typename is not allowed".
struct A
{
template <typename T,typename R> static R f(T x)
{
return (R)x;
}
};
template <class FUNCTION,typename T,typename R> R func()
{
return FUNCTION::f<T,R>(2);
}
int main()
{
return func<A,int,double>();
}
Interestingly enough the following code does compile:
struct A
{
template <typename T> static T f(T x)
{
return x;
}
};
template <class FUNCTION,typename T> T func()
{
return FUNCTION::f(2.f);
}
int main()
{
return func<A,float>();
}
I presume that the second code does compile because the argument of the function provides enough information for GCC to perform template substitution. However I do not understand why the first code fails to compile. So does anybody can explain me why?
You need to use keyword template to tell the compiler that the dependent name f (it depends on the template parameter FUNCTION) is a template name. Only when the compiler knows that's a template name it takes < as the beginning of template-argument-list, otherwise it will try to take < as the less-than operator.
e.g.
return FUNCTION::template f<T,R>(2);
// ~~~~~~~~
The 2nd one works because you didn't use <> (to specify template arguments explicitly).

Confused by template argument deduction

I've created the following struct:
template<class T>
struct not_equals
{
not_equals(T d):data(d){};
bool operator()(T const & in)
{
return data != in;
}
T data;
};
My expectation was that since I need to pass some value of concrete type d to the constructor, template argument T will be deduced from type of d.
However, this does not happens.
not_equals('0'); // fails with 'missing template arguments'
char zero = '0';
not_equals(zero); // same as above
not_equals<char>('0'); // compiles without errors
What is the reason for compiler not recognizing type of template argument?
c++17 will allow class template deduction
Until then, you can create a "make" function:
template <class T> auto make_not_equals(const T& d) -> not_equals<T>
{
return {d};
}
auto z = make_not_equals('0');
This is the C++03 version of the make function:
template <class T> not_equals<T> make_not_equals(const T& d)
{
return not_equals<T>(d);
}
Unfortunately when you declare a variable you need to spell the whole type with the template because of missing auto feature, but the make function can still be helpful in a deduced context, for instance for a parameter:
template <class T> void foo(not_equals<T> ne);
void test()
{
foo(make_not_equals('0'));
}

Need clarification about Lambdas, auto and decltype in SFINAE

I have been trying to learn about SFINAE tricks by reading the following article Link, but having trouble in understanding some parts of it.
Full code: Link
I am confused mostly about these lines of code.
// Check if a type has a serialize method.
auto hasSerialize = is_valid([](auto&& x)
-> decltype(x.serialize()) { });
template <class T> auto serialize(T& obj)
-> typename std::enable_if<decltype(hasSerialize(obj))::value, std::string>::type
{
return obj.serialize();
}
template <class T> auto serialize(T& obj)
-> typename std::enable_if<!decltype(hasSerialize(obj))::value, std::string>::type
{
return to_string(obj);
}
Especially with hasSerialize line and it's use in decltype with an argument. Can anyone tell me what is happing here ? Is hasSerialize is a method ? What is the expression(lambda) to the right side of hasSerialize will evaluates to actually ? What is the order of execution in the evaluation ? What does auto evaluates to in hasSeriaize ?
Please help me to understand this as I am struggling with this for a week, but still can't my head around it. Would appreciate if any one can give an practical example of this.
Thanks
Firstly, this code is using boost::hana::is_valid - make sure to read its documentation and understand what it is doing.
Is hasSerialize is a method?
No, it is a variable initialized with a lambda expression. It is a closure.
What is the expression(lambda) to the right side of hasSerialize will evaluates to actually ?
The following code...
auto hasSerialize = is_valid([](auto&& x) -> decltype(x.serialize()) { });
...will create a function object that when invoked with an object y will return std::true_type if y.serialize() is a valid expression, std::false_type otherwise. Example:
struct Foo { };
struct Bar { void serialize() { } };
static_assert(!hasSerialize(std::declval<Foo>()));
static_assert(hasSerialize(std::declval<Bar>()));
Here's a simple possible implementation of is_valid:
template <typename TF>
struct validity_checker
{
template <typename... Ts>
constexpr auto operator()(Ts... ts)
{
return std::is_callable<
TF(typename decltype(ts)::type...)
>{};
}
};
template <typename TF>
constexpr auto is_valid(TF)
{
return validity_checker<TF>{};
}
It simply uses std::is_callable to see if the generic lambda with a trailing decltype can be called with some particular arguments. If the expression inside the trailing decltype is not valid for some particular argument types, the lambda is not callable.
is_callable can be implemented in a SFINAE-friendly way as follows, using void_t:
template <typename...>
using void_t = void;
template <typename, typename = void>
struct is_callable : std::false_type { };
template <typename TF, class... Ts>
struct is_callable<TF(Ts...),
void_t<decltype(std::declval<TF>()(std::declval<Ts>()...))>>
: std::true_type { };

Concept checking a function doesn't work with movable-only arguments

I have a function that calls a callback function that accepts a movable-only type (for example unique_ptr).
template <typename Function>
void foo(const Function& function) {
BOOST_CONCEPT_ASSERT((
boost::UnaryFunction<Function, void, std::unique_ptr<Bar>));
auto bar = std::make_unique<Bar>();
...
function(std::move(bar));
}
Trying to compile this code, I get a message that the BOOST_CONCEPT_ASSERT line tries to copy the unique_ptr. If I remove the line, the code works fine. It seems that the Boost.Concept library does not support move semantics. Is there any workaround for this without writing my own concept class (which, incidentally, would not be very simple to support both lvalues and rvalues as their arguments).
That's correct. Unfortunately, UnaryFunction as a concept is written as:
BOOST_concept(UnaryFunction,(Func)(Return)(Arg))
{
BOOST_CONCEPT_USAGE(UnaryFunction) { test(is_void<Return>()); }
private:
void test(boost::mpl::false_)
{
f(arg); // "priming the pump" this way keeps msvc6 happy (ICE)
Return r = f(arg);
ignore_unused_variable_warning(r);
}
void test(boost::mpl::true_)
{
f(arg); // <== would have to have std::move(arg)
// here to work, or at least some kind of
// check against copy-constructibility, etc.
}
#if (BOOST_WORKAROUND(__GNUC__, BOOST_TESTED_AT(4) \
&& BOOST_WORKAROUND(__GNUC__, > 3)))
// Declare a dummy construktor to make gcc happy.
// It seems the compiler can not generate a sensible constructor when this is instantiated with a refence type.
// (warning: non-static reference "const double& boost::UnaryFunction<YourClassHere>::arg"
// in class without a constructor [-Wuninitialized])
UnaryFunction();
#endif
Func f;
Arg arg;
};
Since arg is passed by lvalue, there's no way to get that to work with Boost.Concepts. Directly. You could write a hack though. Since we're just calling checking that f(arg) is valid, we could construct a local type for arg that is convertible to unique_ptr<Bar>. That is:
template <typename Function>
void foo(Function f)
{
struct Foo {
operator std::unique_ptr<int>();
};
BOOST_CONCEPT_ASSERT((
boost::UnaryFunction<Function, void, Foo>));
f(std::make_unique<int>(42));
}
Or more generally:
template <typename T>
struct AsRvalue {
operator T(); // no definition necessary
};
template <typename Function>
void foo(Function f)
{
BOOST_CONCEPT_ASSERT((
boost::UnaryFunction<Function, void, AsRvalue<std::unique_ptr<int>>>));
f(std::make_unique<int>(42));
}
That compiles for me on gcc and clang (though gives a warning on clang about unused typedefs). However, at that point, it may be clearer to just write out your own concept to get it to work. Something like Piotr's would be easiest.
#include <type_traits>
#include <utility>
template <typename...>
struct voider { using type = void; };
template <typename... Ts>
using void_t = typename voider<Ts...>::type;
template <typename, typename = void_t<>>
struct is_callable : std::false_type {};
template <typename F, typename... Args>
struct is_callable<F(Args...), void_t<decltype(std::declval<F>()(std::declval<Args>()...))>> : std::true_type {};
//...
static_assert(is_callable<Function&(std::unique_ptr<Bar>)>{}, "Not callable");
DEMO