I've encountered an interesting pattern in the unconstexpr library.
According to the readme, the idea is that given a forward declared auto return type function, the return type can't be deduced before the function is actually defined, and this allows some SFINAE magic.
I thought this language feature could be useful for a few other ideas, and did some experiments with it, and it looks like gcc and clang behaves differently when the function isn't a template: gcc works the same, while clang reports an error.
And based on what the error message of clang shows, maybe it shouldn't work even for a template?
So the questions are:
is the template part (allowed by both clang and gcc) allowed by the standard?
which compiler is correct about the non template version?
Simplified code (also on godbolt):
struct S {};
constexpr auto f1();
template <typename T>
constexpr auto f2(T);
// compilation error with clang:
// function with deduced return type cannot be used before it is defined
template <typename T, typename = decltype(f1())>
void test1_1() {}
template <typename T>
void test1_1() {}
// compiles with clang, even if f2 will never be defined
template <typename T, typename = decltype(f2<T>(T{}))>
constexpr int test1_2(int) { return 1; }
template <typename T>
constexpr int test1_2(float) { return 2; }
/////////////////
int g1_2() {
return test1_2<S>(0);
}
constexpr auto f1() { return 1; }
// comment-uncomment this:
// the value returned by g2_# will change
// /*
template <typename T>
constexpr auto f2(T) {
return 2;
}
// */
// can be defined here
template <typename T, typename = decltype(f1())>
void test2_1() {}
int g2_2() {
return test1_2<S>(0);
}
Note: originally I also mentioned that it behaves differently with friends, and that it seems to be working without an auto return type - those were user errors on my part.
Related
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).
I was experimenting with SFINAE these days, and something puzzles me. Why my_type_a cannot be deduced in my_function's instantiation?
class my_type_a {};
template <typename T>
class my_common_type {
public:
constexpr static const bool valid = false;
};
template <>
class my_common_type<my_type_a> {
public:
constexpr static const bool valid = true;
using type = my_type_a;
};
template <typename T> using my_common_type_t = typename my_common_type<T>::type;
template <typename T, typename V>
void my_function(my_common_type_t<T> my_cvalue, V my_value) {}
int main(void) {
my_function(my_type_a(), 1.0);
}
G++ gives me this:
/home/flisboac/test-template-template-arg-subst.cpp: In function ‘int main()’:
/home/flisboac/test-template-template-arg-subst.cpp:21:30: error: no matching function for call to ‘my_function(my_type_a, double)’
my_function(my_type_a(), 1.0);
^
/home/flisboac/test-template-template-arg-subst.cpp:18:6: note: candidate: template<class T, class V> void my_function(my_common_type_t<T>, V)
void my_function(my_common_type_t<T> my_type, V my_value) {}
^~~~~~~~~~~
/home/flisboac/test-template-template-arg-subst.cpp:18:6: note: template argument deduction/substitution failed:
/home/flisboac/test-template-template-arg-subst.cpp:21:30: note: couldn't deduce template parameter ‘T’
my_function(my_type_a(), 1.0);
^
What I expected was that, when calling my_function as I did in main, T would be deduced to the type of the function's first argument, and that type would be used in the function's instantiation. But it seems that my_common_type_t<T> is instantiated before the function, but even then, the type of my_cvalue would become my_type_a anyways, so I cannot see why this wouldn't work...
Is there a different way to do this? Should I just avoid two (or more) levels of template indirection?
Well, consider this:
template <>
struct my_common_type<int> {
constexpr static const bool valid = true;
using type = my_type_a;
};
template <>
struct my_common_type<double> {
constexpr static const bool valid = true;
using type = my_type_a;
};
// ...
int main(void) {
my_function(my_type_a{}, 1.0);
}
Does the compiler chooses my_common_type<int> or my_common_type<double>?
If the language would permit deduction in you case, it would have to match what T would be in my_common_type<T>::type in order to yield the exact type you send to the function parameter. Obviously, it's not only impossible, but with my example above, it may have multiple choices!
Fortunately, there is a way to tell the compiler that my_common_type<T> will always yield to T. The basics of the trick is this:
template<typename T>
using test_t = T;
template<typename T>
void call(test_t<T>) {}
int main() {
call(1);
}
What is T deduces to? int, easy! The compiler is happy with this kind of match. Also, since test_t cannot be specialized, test_t<soxething> is known to only be something.
Also, this is working too with multiple levels of aliases:
template<typename T>
using test_t = T;
template<typename T>
using test2_t = test_t<T>;
template<typename T>
void call(test2_t<T>) {}
int main() {
call(1); // will also work
}
We can apply this to your case, but we will need some tool:
template<typename T, typename...>
using first_t = T;
This is the same easy match as above, but we can also send some argument that will not be used. We will make sfinae in this unused pack.
Now, rewrite my_common_type_t to still be an easy match, whilst adding the constraint in the unused pack:
template <typename T>
using my_common_type_t = first_t<T, typename my_common_type<T>::type>;
Note that this is also working:
template <typename T>
using my_common_type_t = first_t<T, std::enable_if_t<my_common_type<T>::valid>>;
Now deduction will happen as expected! Live (GCC) Live (Clang)
Note that this trick will only work with C++14, as sfinae in this case (dropped parameters) is only guaranteed to happen since C++14.
Also note that you should either use struct for your trait, or use public: to make the member my_common_type<T>::type public, or else GCC will output a bogus error.
While working on a C++11 type-set, I tried to implement this function (stripped down to the minimum):
constexpr auto test() -> bool;
template <typename T, typename... Rest>
constexpr auto test() -> decltype(test<Rest...>())
{
return {};
}
Both gcc and clang choke on this. Clang says:
test.cpp:54:40: error: 'Rest' does not refer to a value
constexpr auto test() -> decltype(test<Rest...>())
^
gcc complains:
test.cpp:54:44: error: expected primary-expression before ‘...’ token
constexpr auto test() -> decltype(test<Rest...>())
I guess this is because the variadic version of test is not even fully declared when the decltype looks at it.
However, when I use return type deduction in C++14, this compiles just fine:
constexpr auto test() -> bool;
template <typename T, typename... Rest>
constexpr auto test()
{
return test<Rest...>();
}
Seems like test is considered sufficiently declared here.
I wonder why this doesn't work for the decltype variant? Even if I turn on C++14 support?
PS: It turns out, that I cannot really call even the C++14 function, so maybe the whole thing is botched...
One problem is that your first test overload is not a function template, so can't be called with test<>() syntax.
However, decltype doesn't really work with recursive variadic functions like that, since the return type is part of the declaration of the function, so that overload is not declared when the name is looked up.
You can get around this in C++11 by using template classes instead:
template <typename... Ts>
struct test;
template <typename T, typename... Ts>
struct test<T,Ts...> {
static decltype(test<Ts...>::value()) value() { return test<Ts...>::value(); }
};
template <>
struct test<> {
static bool value() { return true; }
};
Live demo
In C++14 you can make the first overload take a single template parameter and the second take two and a parameter pack:
template <typename T>
constexpr auto test() -> bool { return true; }
template <typename T, typename U, typename... Rest>
constexpr auto test()
{
return test<U,Rest...>();
}
Live demo
While the template function itself is being declared, the template function itself has not been declared. So it is not visible to the trailing return type decltype.
You can fix this with ADL. If your template function takes an argument from the same namespace as your template function, the lookup for the return type becomes willing to look at the template function itself. This is because templates do lookup for their return type signature using both the context just before they where declared, and using ADL on each of the parameters.
template<class...Ts> struct types {};
namespace bob{
struct mytag{};
constexpr auto test(mytag, types<>) -> bool{ return true; }
template <typename T, typename... Rest>
constexpr auto test(mytag, types<T,Rest...>)
-> decltype( test( mytag{}, types<Rest...>{} ) )
{
return test(mytag{},types<Rest...>{});
}
}
template<class...Ts>
constexpr auto test()
->decltype( bob::test( bob::mytag{}, types<Ts...>{} ) ){
return bob::test( bob::mytag{}, types<Ts...>{} );
}
You may need constexpr types(){}; and similar for mytag depending on compiler.
This also fixes the fact that test<> is illegal in your original code. Functions do much better with types passed by (tag template wrapped) value in my experience. Overloading is more friendly.
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
With variable templates coming in C++14 (and Clang already supporting them) and a proposal for standard is_same_v and likewise type traits, I figured being able to make new type traits as follows would be neat:
template<typename T>
constexpr bool is_const_and_volatile{std::is_const_v<T> && std::is_volatile_v<T>};
Alas, this results in errors equivalent to the following SSCCE (this one contains everything mentioned below):
#include <type_traits>
template<typename T>
constexpr bool is_pointer{std::is_pointer<T>::value};
template<typename T>
constexpr bool foo{is_pointer<T>};
int main() {
//foo<int *>;
}
With the line in main commented, Clang spits out the following:
warning: variable is_pointer<type-parameter-0-0> has internal linkage but is not defined
It looks defined to me (note that changing T to int * in foo works fine). Uncommenting the line in main to instantiate foo gives this (again, T to int * works fine):
error: constexpr variable foo<int *> must be initialized by a constant expression
However, replacing foo with the following old syntax causes both instances to work fine:
constexpr bool foo{std::is_pointer<T>::value};
Is there something I'm missing about variable templates? Is there a way to build new variable templates with them, or am I forced to use the older syntax to build new ones and only enjoy the syntactic sugar when using them for other code?
Your code is valid, and is accepted by clang SVN. The link error was caused by clang bug 17846, which I fixed a couple of days ago.
The following seems to work:
#include <type_traits>
#include <iostream>
template<typename T>
struct test {
static constexpr bool is_pointer{std::is_pointer<T>::value};
};
template<typename T>
constexpr bool test<T>::is_pointer;
template<typename T>
constexpr bool foo{test<T>::is_pointer};
int main() {
std::cout << foo<bool>;
std::cout << foo<bool*>;
}
Live Example
Although it procs the same warning if used in a constexpr context so I suppose it doesn't really work after all.
// Fail
template<typename T>
typename std::enable_if<foo<T>, void>::type bar()
{
}
int main() {
bar<bool*>();
}
main.cpp:21:5: error: no matching function for call to 'bar'
bar<bool*>();
^~~~~~~~~~
main.cpp:16:45: note: candidate template ignored: substitution failure [with T = bool *]: non-type template argument is not a constant expression
typename std::enable_if<foo<T>, void>::type bar()
It does stop complaining if you give foo an explicit type:
template<typename T>
typename std::enable_if<foo<bool*>, void>::type bar()
{
}
Or just use test<T>::is_pointer directly:
template<typename T>
typename std::enable_if<test<T>::is_pointer, void>::type bar()
{
}