Is this a compiler bug or a programmer bug? - c++

I'm playing with tuples as compile time lists. In How can I have multiple parameter packs in a variadic template? I answered myself with some code that works in both GCC and Clang, but Clang wont compile now that I've added (what I think is) perfect forwarding. It complains that As... and as... have different lengths in std::forward<As>(as).... How can this be true when As... is the type of as...? It's As&&... as in the parameters.
#include <iostream>
#include <tuple>
template < typename ... >
struct two_impl {};
// Base case
template < typename F,
typename ...Bs >
struct two_impl < F, std::tuple <>, std::tuple< Bs... > > {
void operator()(F&& f, Bs&&... bs) {
f(std::forward<Bs>(bs)...);
}
};
// Recursive case
template < typename F,
typename A,
typename ...As,
typename ...Bs >
struct two_impl < F, std::tuple< A, As... >, std::tuple< Bs...> > {
void operator()(F&& f, A&& a, As&&... as, Bs&&... bs) {
auto impl = two_impl < F, std::tuple < As&&... >, std::tuple < Bs&&..., A&& > >();
impl(std::forward<F>(f), std::forward<As>(as)..., std::forward<Bs>(bs)..., std::forward<A>(a));
}
};
template < typename F, typename ...Ts >
void two(F&& f, Ts&& ...ts) {
auto impl = two_impl< F, std::tuple < Ts... >, std::tuple <> >();
impl(std::forward<F>(f), std::forward<Ts>(ts)...);
}
struct Test {
void operator()(int i, float f, double d) {
std::cout << i << std::endl << f << std::endl << d << std::endl;
}
};
int main () {
two(Test(), 1, 1.5f, 2.1);
}
Compiling with clang -lstdc++ -std=c++0x multiple_parameter_packs.cpp
clang -lstdc++ -std=c++0x multiple_parameter_packs.cpp
multiple_parameter_packs.cpp:24:50: error: pack expansion contains parameter packs 'As' and 'as' that have different
lengths (1 vs. 2)
impl(std::forward<F>(f), std::forward<As>(as)..., std::forward<Bs>(bs)..., std::forward<A>(a));
~~ ~~ ^
multiple_parameter_packs.cpp:24:5: note: in instantiation of member function 'two_impl<Test, std::tuple<float &&,
double &&>, std::tuple<int &&> >::operator()' requested here
impl(std::forward<F>(f), std::forward<As>(as)..., std::forward<Bs>(bs)..., std::forward<A>(a));
^
multiple_parameter_packs.cpp:31:3: note: in instantiation of member function 'two_impl<Test, std::tuple<int, float,
double>, std::tuple<> >::operator()' requested here
impl(std::forward<F>(f), std::forward<Ts>(ts)...);
^
multiple_parameter_packs.cpp:41:3: note: in instantiation of function template specialization
'two<Test, int, float, double>' requested here
two(Test(), 1, 1.5f, 2.1);
^
1 error generated.
Compilation exited abnormally with code 1 at Fri Mar 23 14:25:14

This appears to be a bug in an old version of Clang. The code works fine with trunk Clang, with either libstdc++ or libc++.
$ clang++ multiple_parameter_packs.cpp -std=c++11 -stdlib=libc++
$ ./a.out
1
1.5
2.1

I don't think that this:
void operator()(F&& f, A&& a, As&&... as, Bs&&... bs)
is quite possible.
A parameter pack should be the last argument, and As&&... as is followed by another pack here.

Related

Auto return type of template and ambiguity

I have an overloaded template function:
template<typename T1, typename T2>
auto overMax(T1 a, T2 b)
{
std::cout << __FUNCSIG__ << std::endl;
return b < a ? a : b;
}
template<typename RT, typename T1, typename T2>
RT overMax(T1 a, T2 b)
{
std::cout << __FUNCSIG__ << std::endl;
return b < a ? a : b;
}
If I call it like this:
auto a = overMax(4, 7.2); // uses first template
auto b = overMax<double>(4, 7.2); // uses second template
everything works perfect, but
auto c = overMax<int>(4, 7.2); // error
causes ambiguous call.
Why is it so with int, and OK which other types?
RT is non deducible, so when not providing it, only template<typename T1, typename T2>
auto overMax(T1 a, T2 b) can be called.
When you (partially) provide one template argument, both methods are viable,
but depending of argument, one can be a better candidate:
For auto b = overMax<double>(4, 7.2); // uses second template
Both overMax<double, int, double> and overMax<double, double> are viable.
But overMax<double, int, double> is exact match
whereas overMax<double, double> requires int to double conversion.
For auto c = overMax<int>(4, 7.2); // Ambiguous call
Both overMax<int, int, double> and overMax<int, double> are viable.
But neither is a better match or more specialized, so the call is ambiguous.

Parameter with non-deduced type after parameter pack

There is different behaviour in clang++ and g++ for the next program:
#include <type_traits>
#include <utility>
template< std::size_t index, typename type >
struct ref { type & value; };
template< std::size_t index, typename type >
type && get(ref< index, type > const & r)
{
return std::forward< type >(r.value);
}
template< typename F, typename ...types, std::size_t ...indices >
decltype(auto) apply_inverse(F & f, types &... values, std::index_sequence< indices... >)
{
struct : ref< indices, types >... {} refs{{values}...};
constexpr std::size_t top = sizeof...(indices) - 1;
return std::forward< F >(f)(get< top - indices >(refs)...);
}
template< typename F, typename ...types >
decltype(auto) apply_inverse(F && f, types &&... values)
{
return apply_inverse< F, types... >(f, values..., std::index_sequence_for< types... >{});
}
#include <iostream>
int main()
{
auto const print = [] (auto const &... value) -> std::ostream & { return (std::cout << ... << value); };
apply_inverse(print, 1, 2, 3) << std::endl;
}
Live example.
It just tries to revert the arguments passed and applies some function to them.
For G++ it compiles fine, but for clang++ (even from trunk) it gives the following error:
error: no matching function for call to 'apply_inverse'
I think the reason is the fact, that in upper overloading there is a parameter after parameter pack in the function prototype. But types for all the arguments in arguments pack are explicitly specified.
Is it right for compiler to accept the code?
It was not specified exactly what version of Clang refused the code above.
But at this moment Clang 12 accepts it, as well as GCC and MSVC:
https://gcc.godbolt.org/z/qMc9fKTEf
So the code is perfectly legal.

Why std::is_function returns false for simple functions and lambdas?

Having the following piece of code:
#include <iostream>
#include <type_traits>
template <typename F,
typename = typename std::enable_if<
std::is_function< F >::value
>::type>
int fun( F f ) // line 8
{
return f(3);
}
int l7(int x)
{
return x%7;
}
int main()
{
auto l = [](int x) -> int{
return x%7;
};
fun(l); // line 23
//fun(l7); this will also fail even though l7 is a regular function
std::cout << std::is_function<decltype(l7)>::value ; // prints 1
}
I will get the following error:
main2.cpp: In function ‘int main()’:
main2.cpp:23:8: error: no matching function for call to ‘fun(main()::<lambda(int)>&)’
fun(l);
^
main2.cpp:8:5: note: candidate: template<class F, class> int fun(F)
int fun( F f )
^
main2.cpp:8:5: note: template argument deduction/substitution failed:
main2.cpp:5:11: error: no type named ‘type’ in ‘struct std::enable_if<false, void>’
typename = typename std::enable_if<
^
When I comment out the std::enable_if template parameter then it compiles and runs just fine. Why?
From cppreference:
Checks whether T is a function type. Types like std::function, lambdas, classes with overloaded operator() and pointers to functions don't count as function types.
This answer explains that you also need to use std::remove_pointer<F>::type as the type since functions are converted to pointers to functions when passing by value. So your code should look like this:
template <typename F,
typename = typename std::enable_if<
std::is_function<
typename std::remove_pointer<F>::type
>::value
>::type>
int fun( F f )
{
return f(3);
}
Another way to approach this problem is to write a more specific type trait. This one, for example, checks that the argument types are convertible and works for anything that's callable.
#include <iostream>
#include <type_traits>
#include <utility>
#include <string>
template<class T, class...Args>
struct is_callable
{
template<class U> static auto test(U*p) -> decltype((*p)(std::declval<Args>()...), void(), std::true_type());
template<class U> static auto test(...) -> decltype(std::false_type());
static constexpr auto value = decltype(test<T>(nullptr))::value;
};
template<class T, class...Args>
static constexpr auto CallableWith = is_callable<T, Args...>::value;
template <typename F,
std::enable_if_t<
CallableWith<F, int>
>* = nullptr
>
int fun( F f ) // line 8
{
return f(3);
}
int l7(int x)
{
return x%7;
}
int main()
{
auto l = [](int x) -> int{
return x%7;
};
std::cout << "fun(l) returns " << fun(l) << std::endl;
std::cout << CallableWith<decltype(l7), int> << std::endl; // prints 1
std::cout << CallableWith<decltype(l7), float> << std::endl; // prints 1 because float converts to int
std::cout << CallableWith<decltype(l7), const std::string&> << std::endl; // prints 0
}
Have a look at std::is_invocable which also covers lambdas in C++17 (std::is_callable does not exist).

Generic lambda functions with template parameters

#include <utility>
#include <tuple>
template < typename T, typename U >
void h(T, U)
{
}
template < typename... T, typename... U >
void f(std::tuple < T... > t, std::tuple < U... > u)
{
auto g = [&] < std::size_t... I > (std::index_sequence < I... >)
{
bool const r[]{((void)h(std::get < I >(t), std::get < I >(u)), false)...};
(void)r;
};
g(std::index_sequence_for < T... >());
}
int main()
{
f(std::make_tuple(0L, 0LL), std::make_tuple(0UL, 0ULL));
}
The above compiles with g++ test_templated_lambda.cpp -o test_templated_lambda -std=c++14, but doesn't compile with clang++ test_templated_lambda.cpp -o test_templated_lambda -std=c++14
I know it is a GCC extension (Using template parameter in a generic lambda), but is there some way to do this without writing out g as a free function
This is not possible without some external help; generic lambdas are the only form of template permitted within a function scope, and they cannot be specialized or overloaded (without some external helper, such as P0051R1 overload).
It is possible to write a compile-time loop by embedding a recursive generic lambda in your function, but (a) you'll have to convert it to fix-point combinator form, and (b) terminating it is seriously ugly:
[&](auto&& g) {
g(g, std::integral_constant<std::size_t, 0u>{});
}([&](auto&& g, auto I) {
h(std::get<I>(t), std::get<I>(u));
std::get<I + 1 == sizeof...(T)>(std::make_pair(
[&](auto&& g) { g(g, std::integral_constant<std::size_t, I + 1>{}); },
[](auto&&){}))(g);
});
Example.
You're already using an external helper facility (std::index_sequence_for), so why not write another? For example:
template<class F, std::size_t... I> auto apply_indexes(F&& f, std::index_sequence<I...>) {
return std::forward<F>(f)(std::integral_constant<std::size_t, I>{}...);
}
Usage:
auto g = [&](auto... I)
{
bool const r[]{((void)h(std::get<I>(t), std::get<I>(u)), false)...};
(void)r;
};
apply_indexes(g, std::index_sequence_for<T...>());

different behaviour of enabler in different contexts in class templates

Why the behaviour is so different? #if 1 version successfully (that is weird) compiles and produces the expected output to stdout, but version with #if 0 does not:
#include <iostream>
#include <type_traits>
#include <cstdlib>
template< typename T >
class X
{
struct B {};
public :
struct U : B {};
};
template< typename T >
struct Y
{
using X_type = X< T >;
template< typename D,
typename = typename std::enable_if< std::is_base_of< typename X_type::B, D >::value >::type >
void operator () (D const &) const
{
std::cout << "allowed only for derived from B" << std::endl;
}
};
template< typename T >
struct Z
{
using X_type = X< T >;
template< typename D >
auto
operator () (D const &) const
-> typename std::enable_if< std::is_base_of< typename X_type::B, D >::value >::type
{
std::cout << "allowed only for derived from B!" << std::endl;
}
};
int main()
{
using T = struct W; // not matters
using X_type = X< T >;
using U = typename X_type::U;
#if 0
using V = Y< T >;
#else
using V = Z< T >;
#endif
V v;
v(U());
return EXIT_SUCCESS;
}
This generates an error (essentially 'B' is private in accordance with my expectations (in fact) for both cases):
<stdin>: In function 'int main()':
<stdin>:60:10: error: no match for call to '(V {aka Z<main()::W>}) (U)'
<stdin>:34:8: note: candidate is:
<stdin>:41:5: note: template<class D> typename std::enable_if<std::is_base_of<typename X<T>::B, D>::value>::type Z<T>::operator()(const D&) const [with D = D; T = main()::W]
<stdin>:41:5: note: template argument deduction/substitution failed:
<stdin>: In substitution of 'template<class D> typename std::enable_if<std::is_base_of<typename X<T>::B, D>::value>::type Z<T>::operator()(const D&) const [with D = D; T = main()::W] [with D = X<main()::W>::U]':
<stdin>:60:10: required from here
<stdin>:10:12: error: 'struct X<main()::W>::B' is private
<stdin>:41:5: error: within this context
g++ -v output contains the following line:
gcc version 4.8.1 (rev3, Built by MinGW-builds project)
I expect that the B is not accessible at all from anywere except X.
The problem take place exceptionally only for the class templates, but not for pure classes (here we have two identical problems, because both variants do not have to be compiled, but they are compiled).