C++ why does SFINAE fail with only a class template parameter? - c++

I'm using SFINAE in the style of this answer in order to call a generic vector object by using an appropriate member function. For example, the following code calls operator[](int) const first, and if that doesn't exist then operator()(int) const:
template<int I> struct rank : rank<I-1> { static_assert(I > 0, ""); };
template<> struct rank<0> {};
template<typename VectorType>
struct VectorWrapper
{
auto get(int i) const
{
return get(v, i, rank<5>());
}
template<typename V, typename = std::enable_if_t<has_bracket_operator<const V>::value> >
auto get(V const& v, int i, rank<2>) const
{
return v[i];
}
template<typename V, typename = std::enable_if_t<has_parenthesis_operator<const V>::value> >
auto get(V const& v, int i, rank<1>) const
{
return v(i);
}
VectorType v;
};
With the has_bracket_operator and has_parenthesis_operator traits set up as suggested in this thread, the whole compiles and seems to work.
However, passing the member vector to the overloaded class templates seems unnecessary from the first, so I tried to set up the same without passing it. For this, I replaced the template parameter V with the VectorType parameter used to set up the class template:
template<typename = std::enable_if_t<has_bracket_operator<VectorType>::value> >
auto get(int i, rank<2>) const
{
return v[i];
}
template<typename = std::enable_if_t<has_parenthesis_operator<VectorType>::value> >
auto get(int i, rank<1>) const
{
return v(i);
}
Now, however, the compilation fails (in gcc 5.1.0) with the following error message:
/usr/local/include/c++/5.1.0/type_traits: In substitution of 'template<bool _Cond, class _Tp> using enable_if_t = typename std::enable_if::type [with bool _Cond = has_parenthesis_operator<std::vector<int> >::value; _Tp = void]':
main.cpp:46:10: required from 'struct VectorWrapper<std::vector<int> >'
main.cpp:59:38: required from here
/usr/local/include/c++/5.1.0/type_traits:2388:61: error: no type named 'type' in 'struct std::enable_if<false, void>'
using enable_if_t = typename enable_if<_Cond, _Tp>::type;
DEMO
Questions:
What is the reason for this compilation error?
Is there an appropriate workaround other than that of my first code block? (That is, one that retains the usual coding style -- where one does not have to pass members).

SFINAE comes to us from [temp.deduct]/8, emphasis mine:
If a substitution results in an invalid type or expression, type deduction fails. An invalid type or expression is
one that would be ill-formed, with a diagnostic required, if written using the substituted arguments. [ Note:
If no diagnostic is required, the program is still ill-formed. Access checking is done as part of the substitution
process. —end note ] Only invalid types and expressions in the immediate context of the function type and
its template parameter types can result in a deduction failure.
The immediate context is what's in the template declaration. In your initial example:
template<typename V, typename = std::enable_if_t<has_bracket_operator<const V>::value> >
auto get(V const& v, int i, rank<2>) const
V is in the immediate context, so a substitution failure on the enable_if is just a deduction failure.
However, in your second example:
template<typename = std::enable_if_t<has_bracket_operator<VectorType>::value> >
auto get(int i, rank<2>) const
VectorType is not in the immediate context of get, so a failure here would not be a deduction failure, it would be a hard error.
Unless VectorType happens to have all of these operators.
The solution to any template problem is to just add more template. In this case, force VectorType to be in the immediate context by introducing another type:
template<typename T=VectorType, typename = std::enable_if_t<has_bracket_operator<T>::value> >
auto get(int i, rank<2>) const
And call get<>().

In your failing example, the template parameter VectorType has already been determined by the time get is being resolved. To make SFINAE work, you need to make the template parameters you are using for SFINAE resolve at that method call. The following is a modification of your first example to work like you want to:
template<int I> struct rank : rank<I-1> { static_assert(I > 0, ""); };
template<> struct rank<0> {};
template<typename VectorType>
struct VectorWrapper
{
auto get(int i) const
{
return get(v, i, rank<5>());
}
template<typename V=VectorType, typename = std::enable_if_t<has_bracket_operator<const V>::value> >
auto get(int i, rank<2>) const
{
return v[i];
}
template<typename V=VectorType, typename = std::enable_if_t<has_parenthesis_operator<const V>::value> >
auto get(int i, rank<1>) const
{
return v(i);
}
VectorType v;
};
This way, V is resolved when get is called, and it will correctly use SFINAE.

Or you can just use tag-dispatching:
auto get(int i) const
{
return get(i, has_bracket_operator<VectorType>(), has_parenthesis_operator<VectorType>());
}
auto get(int i, std::true_type /*brackets*/, std::false_type /*parenthesis*/) const
{
return v[i];
}
auto get(int i, std::false_type /*brackets*/, std::true_type /*parenthesis*/) const
{
return v(i);
}
demo

Related

Recursive variadic template with empty parameter pack (to avoid duplication for base case)

I am experimenting with C++ recursive templates and I do not know why my template is not working.
Say I want to define a recursive function that takes a variable number of arguments (for different types).
I've have looked at many examples of variadic templates, and all that I've seen so far use a separate template specialisation to specify the base case.
However, I think it would be nicer (in some cases at least) to use a single template, that defines the base case as well as the recursive cases.
I think this approach is especially nice if you have a lot of common logic in the function, which you would have to duplicate for your base case instance (exact same code in two different places).
The second template in the example below is supposed to be my solution. I would think that this template should be functioning on it's own. However this is not the case.
Without the first template, the code does not compile:
error: no matching function for call to
'add_elems'
return head[i] + add_elems(i, second, tail...);
^~~~~~~~~
in instantiation of function
template specialization 'add_elems<double, std::__1::vector<double, std::__1::allocator<double> >>' requested here
...
Apparently the template braks when tail consists of just one parameter. But shouldn't add_elems(i, second, tail...) then still be valid for the template
template<typename V, typename S, typename... T>
V add_elems(size_t i, const std::vector<V>& head, const S& second, const T&... tail) with an empty tail?
I do not know if this is compiler dependent, but I am using clang.
#include <iostream>
#include <vector>
/* This template is the exact same as the below template with an
empty parameter pack as tail. I want my code to be working
without this specialisation */
template<typename V, typename S>
V add_elems(size_t i, const std::vector<V>& head, const S& second)
{
/* Imagine some more code here */
return head[i] + second[i];
}
template<typename V, typename S, typename... T>
V add_elems(size_t i, const std::vector<V>& head, const S& second, const T&... tail)
{
/* Imagine some more code here (the same as above) */
if (sizeof...(tail) > 0)
return head[i] + add_elems(i, second, tail...);
else
return head[i] + second[i];
}
int main()
{
std::vector<double> a({1, -3, -3});
std::vector<double> b({2, -2, 1});
std::vector<double> c({4, -4, -11});
std::vector<double> d({4, 10, 0});
std::cout << "Result: " << add_elems(0, a, b, c, d);
std::cout << " ," << add_elems(1, a, b, c, d);
std::cout << " ," << add_elems(2, a, b, c, d);
}
The problem is that your if statement is not constexpr. Meaning that all code paths need to be compilable for every potential call to add_elems
This means that eventually you end up at a case where tail is just one element, and the compiler needs to evaluate add_elems(size_t&, const, std::vector<double>&), which doesn't exist because there's no second argument.
If you were able to have a constexpr if statement, then this would all work nicely because the compiler wouldn't even compile the bad branch when it evaluates to false, and therefore wouldn't look for a nonexistent function:
template<typename V, typename S, typename... T>
V add_elems(size_t i, const std::vector<V>& head, const S& second, const T&... tail)
{
if constexpr (sizeof...(tail) > 0)
return head[i] + add_elems(i, second, tail...);
else
return head[i] + second[i];
}
Demo (requires Clang 3.9.1 or greater and -std=c++1z option.)
For what it's worth, if you have access to C++17, you can achieve this with a unary right fold:
template<typename... T>
decltype(auto) add_elems(size_t i, const T&... elems)
{
return (elems[i] + ...);
}
Demo 2 (requires Clang 3.6.0 or greater and -std=c++1z option.)
Waiting for C++17, I propose a C++11 not-so-nice solution, following the AndyG one
template <typename T0, typename ... T>
auto add_elems2 (size_t i, T0 const & elem0, T const & ... elems)
-> decltype(elem0[i])
{
using unused=int[];
auto ret = elem0[i];
unused a { (ret += elems[i], 0)... };
return ret;
}
As the error message says, the problem you have at the moment is that the call add_elems(i, second, tail...) doesn't match the definition of the function, in the case where tail is empty. Even though the boolean expression in the if statement is constexpr, until c++1z the whole body of the function has to be valid.
#AndyG provides one way that c++1z can deal with this issue, another is with if constexpr, which allows a "compile time branch". Either of those allow you to have one (primary) specialisation of your template.
// Only in c++1z
template<typename V, typename S, typename... T>
V add_elems(size_t i, const std::vector<V>& head, const S& second, const T&... tail)
{
/* Imagine some more code here (the same as above) */
if constexpr (sizeof...(tail) > 0)
return head[i] + add_elems(i, second, tail...); // this is not required to be valid when the if is false
else
return head[i] + second[i]; // this is not required to be valid when the if is true (but it is happens to be valid anyway)
}
You can use Boost.Hana to emulate the behaviour of if constexpr in C++14. For example:
template <typename...>
struct is_empty_pack : hana::integral_constant<bool, false> {};
template <>
struct is_empty_pack<> : hana::integral_constant<bool, true> {};
template <typename T, typename... Ts>
auto sum(T const& t, Ts const&... ts) {
return hana::if_(is_empty_pack<Ts...>{},
[](auto const& t) { return t; },
[](auto const& t, auto const&... ts) { return t + sum(ts...); }
)(t, ts...);
}
As many have noted, this is easy in C++1z. It can be done in C++14, it is just hard.
template<class True, class False>
True pick( std::true_type, True t, False ) {
return std::move(t);
}
template<class True, class False>
False pick( std::false_type, True, False f ) {
return std::move(f);
}
template<bool b>
constexpr std::integral_constant<bool, b> bool_k;
template<typename V, typename S, typename... T>
V add_elems(size_t i, const std::vector<V>& head, const S& second, const T&... tail)
{
return
pick( bool_k<(sizeof...(tail)>0)>,
[&](const auto&... tail)->V{
// tail... template argument hides function argument above:
return head[i] + add_elems(i, second, tail...);
},
[&]()->V{
return head[i] + second[i];
}
)
( tail... );
};
we do a compile time dispatch using pick to one of two lambdas.
These lambdas take the part of the code that varies by auto parameter, which makes them templates. So long as they are valid for some set of auto parameters (even ones they are "never called with"), they are legal C++.
What we have readly done is hide the two overloads within the lambdas. As C++11 doesn't have template lambdas, this "hidden overload" technique won't work in C++11.

Recursively check if function method exists using template

For reasons I want to be able to do this;
vector<int> p = {1, 2};
vector<vector<int>> q = {p, {0, 1}};
auto t = test(p);
auto u = test(q); // Fails with below implementation
Where notably test is templated to accept custom classes which might or might not be iterable for one or two (for now) dimensions. I'm trying to determine what to do by checking if whatever was given has a size function;
template<typename T> struct hasSize {
template<typename U, size_t(U::*)() const> struct SFINAE {};
template<typename U> static char Test(SFINAE<U, &U::size>*);
template<typename U> static int Test(...);
static const bool value = sizeof(Test<T>(0)) == sizeof(char);
};
template<typename iterable> int test(const iterable &x, std::false_type) {
return (int) x;
}
template<typename iterable> int test(const iterable &x, std:: true_type) {
int total = 0;
for(auto &each : x)
total += test(each,
std::integral_constant<bool, hasSize<decltype(each)>::value>());
return total;
}
template<typename iterable> int test(const iterable &view) {
return test(view, std::true_type());
}
I based hasSize on the answers given here after giving up on this answer, since that seemed to be only applicable to member variables, not functions. I also tried a modified version of has_const_reference_op given in first discussion, but this has the same problem.
The error given suggests SNIFAE is not applied a second time;
error C2440: 'type cast':
cannot convert from 'const std::vector<int, std::allocator<_Ty>>' to 'int'
note: No user-defined-conversion operator available that can perform this conversion,
or the operator cannot be called
note: see reference to function template instantiation
'int test<iterable>(const iterable &, std::false_type)' being compiled
with iterable = std::vector<int,std::allocator<int>>
But I have no idea why.
The reason why it fails is that the auto&-typed variable is actually of type const std::vector<int>& with iterable of type const vector<vector<int>>&, and so when queried with decltype -- it yields a reference type that fails the SFINAE check for the size member function existance. So instead of using decltype, just read the value_type from iterable:
total += test(each, std::integral_constant<bool,
hasSize<typename iterable::value_type>::value
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^
>());
or remove referenceness/constness from the type produced by decltype:
total += test(each, std::integral_constant<bool,
hasSize<typename std::decay<decltype(each)>::type>::value
// ~~~~~~~~~^
>());
First of all, this isn't the right thing for the general test():
template<typename iterable> int test(const iterable &view) {
return test(view, std::true_type());
}
Since in general test isn't iterable - that's what we need to test for! So instead, we're going to forward this to a series of three functions in a namespace so that we can take advantage of ADL to find everything that we need to find:
namespace adl {
struct helper {};
template<typename iterable>
int test(helper, const iterable &x, std::false_type) {
return (int) x;
}
template<typename iterable>
int test(helper, const iterable &x, std:: true_type) {
int total = 0;
for(auto &each : x) {
// this calls the general one
total += test(helper{}, each);
}
return total;
}
template <typename iterable>
int test(helper, const iterable& x) {
return test(helper{}, x, std::integral_constant<bool, hasSize<iterable>::value>{});
}
}
template<typename iterable>
int test(const iterable &view) {
return test(adl::helper{}, view);
}
We need ADL so that each of the functions can find each other.
Note that it's better to write type traits that yield types instead of just values. Had we written something like:
template <class T, class = void>
struct hasSize : std::false_type { };
template <class T>
struct hasSize<T, void_t<decltype(std::declval<T const&>().size())>>
: std::true_type { };
then our tester overload could be much shorter:
template <typename iterable>
int test(helper, const iterable& x) {
return test(helper{}, x, hasSize<iterable>{});
}
This may not work on VS2013, but you could still add a type typedef to your hasSize.

counting nested types in pairs and tuples

I need to count types in nested pairs and tuples and have come up with this snippet. If I use gcc's declval(), however, the following error occurrs:
/usr/include/c++/5.3.0/type_traits:2204:7: error: static assertion failed: declval() must not be used!
static_assert(__declval_protector<_Tp>::__stop,
struct swallow
{
template <typename ...T>
explicit swallow(T&& ...) noexcept
{
}
};
template <typename C>
constexpr inline decltype(auto) declval() noexcept
{
return ::std::move(*static_cast<C*>(nullptr));
}
template <typename T>
constexpr inline auto count_types(T const&) noexcept
{
return 1;
}
template <typename ...A>
constexpr inline auto count_types(::std::pair<A...> const&) noexcept
{
int r{};
swallow{(r += count_types(declval<A>()))...};
return r;
}
template <typename ...A>
constexpr inline auto count_types(::std::tuple<A...> const&) noexcept
{
int r{};
swallow{(r += count_types(declval<A>()))...};
return r;
}
int main()
{
::std::cout << count_types(declval<::std::tuple<int, int, ::std::pair<int, ::std::pair<int, int>>>>());
return 0;
}
My question is why and why does my declval implementation work correctly, while gcc's doesn't? I'll try to solve the problem using meta template programming next.
EDIT: Here's a fix, that compiles with both gcc and clang:
struct swallow
{
template <typename ...T>
constexpr explicit swallow(T&& ...) noexcept
{
}
};
template <typename C>
constexpr inline decltype(auto) declval() noexcept
{
return static_cast<typename std::remove_reference<C>::type&&>(*static_cast<C*>(nullptr));
}
template <typename T>
constexpr inline auto count_types(T const&) noexcept
{
return 1;
}
template <typename ...A>
constexpr inline auto count_types(::std::pair<A...> const&) noexcept
{
int r{};
swallow{(r += count_types(declval<A>()))...};
return r;
}
template <typename ...A>
constexpr inline auto count_types(::std::tuple<A...> const&) noexcept
{
int r{};
swallow{(r += count_types(declval<A>()))...};
return r;
}
int main()
{
::std::cout << ::std::integral_constant<int, count_types(declval<::std::tuple<int, int, ::std::pair<int, ::std::pair<int, int>>>>())>{};
return 0;
}
std::declval is intended exclusively for compile-time logic. It is defined as, from [declval]:
template <class T>
add_rvalue_reference_t<T> declval() noexcept; // as unevaluated operand
There is no body to that function. It's just there to give you a type. You cannot do runtime operations with it. That's not what it's for.
Your implementation of declval involves explicitly dereferencing a null pointer, at runtime. That's just a misunderstanding of what declval is for and how to use it. Additionally, your implementation of declval doesn't support lvalue references (e.g. I can std::declval<Foo&>() gives me an lvalue reference, your_declval<Foo&>() is ill-formed since you're taking a pointer to a reference).
The way to write something like this would be based on types. You can still write a function that takes a std::tuple<A...> and returns the number of nested types - it's just that that cannot be a runtime operation, it should encode that result into a type. That is, we start with:
struct adl_tag {};
template <class T>
inline auto count_types_impl(adl_tag, T const&)
-> std::integral_constant<int, 1>;
template <class T>
using count_types = decltype(count_types_impl(adl_tag{}, std::declval<T>()));
And then just add other overloads for count_types_impl() for the other types we wish to support. The adl_tag is there so we can find all the overloads we need. Using fold-expressions to make this answer shorter, we can then add:
template <class... A>
inline auto count_types_impl(adl_tag , std::tuple<A...> const&)
-> std::integral_constant<int, (count_types<A>::value + ...)>;
template <class A, class B>
inline auto count_types_impl(adl_tag , std::pair<A,B> const&)
-> std::integral_constant<int, (count_types<A>::value + count_types<B>::value)>;
So that:
std::cout << count_types<std::tuple<int, std::pair<int,int>>>::value; // prints 3
Note that at no point is anything evaluated. None of these functions even have a body!
std::declval is not supposed to return an actual value, and it seems libstdc++ guards against it further to give you a more helpful message than a linker error would provide.
Note that because no definition exists for declval, it can only be used in unevaluated contexts; it is an error to evaluate an expression that contains this function. Formally, the program is ill-formed if this function is odr-used.
-- cppreference
Also, this: return ::std::move(*static_cast<C*>(nullptr)); seems hardly safe, dereferencing a null pointer is undefined behavior...

C++ variadic template

I am trying to create a polymorhic container working with variadic templates.
Container is initialized as
container<tag, std::string, int, int, int> m;
I want to use following syntax:
auto& v2 = m.find<2, 3, 4>(255, 0, 0);
Template arguments would specify "columns" and for parameters, I want appropriate type to be expected by compiler.
For one template argument (find<2>(255)) I used:
template < int idx > const typename value_type &
find( const typename std::tuple_element<idx, typename value_type>::type &key) {
const std::size_t row_id = getId<idx>(key);
return data_.at(row_id);
}
That worked perfectly, so I wanted to expand it as follows:
template<int ... idx> const typename value_type &
find(const typename std::tuple_element<idx..., typename value_type>::type &keys...) {
const std::size_t row_id = getId<idx...>(keys);
return data_.at(row_id);
}
What's not working at all. Compilation error C2660 - find: function does not take 3 arguments. Can someone explain me, what am I missing here?
Thanks.
EDIT:
Header of container class is
template<typename ... Arguments> class container
value_typementioned is
typedef std::tuple < Arguments... > value_type;
EDIT2:
T.C.'s answer was indeed useful, though I'm still crawling through my bugs with variadic templates. Currently:
enum tag {/*...*/}
int main() {
container<tag, std::string, int, int, int> m;
}
template<typename ... Arguments> class container {
public:
typedef std::tuple < Arguments... > value_type;
std::vector<value_type> data_;
template <int id> void narrowRange(
std::set<std::size_t> & range,
const typename std::tuple_element<id, typename value_type>::type &key)
{
// all commented out
}
template <int id, int ... idx>
void narrowRange(
std::set<std::size_t> & range,
const typename std::tuple_element<id, typename value_type>::type & key,
const typename std::tuple_element<idx, typename value_type>::type & ... keys) // <-
{
narrowRange<idx...>(range, keys...);
// rest commented out
}
Will invoke internal error in MSVS2013 on the marked line. Any suggestions why would be appreciated.
First, value_type doesn't need typename - I'm fairly sure the grammar actually bans it.
Second, you are expanding idx too early, and also incorrectly attempting to expand keys in the declaration. (That second ... is actually being parsed as a C-style varargs.) You are also not expanding the pack keys in the function body. Assuming that you want find<2, 3, 4>(255, 0, 0) to call getId<2, 3, 4>(255, 0, 0), the correct syntax is
template<int ... idx> const value_type &
find(const typename std::tuple_element<idx, value_type>::type &... keys) {
const std::size_t row_id = getId<idx...>(keys...);
return data_.at(row_id);
}

Understanding SFINAE Example

I'm having trouble understanding this snippet of code which uses SFINAE.
template <typename T>
auto dist() -> typename std::enable_if<std::is_integral<T>::value,
std::uniform_int_distribution<T>>::type;
template <typename T>
auto dist() -> typename std::enable_if<std::is_floating_point<T>::value,
std::uniform_real_distribution<T>>::type;
...
decltype(dist<float>()) unifDistFloat;
decltype(dist<int>()) unifDistInt;
dist() is the name of two different function prototypes, so there is no body containing a return statement. Meaning it never actually return a value of the type uniform_real_distribution<T>, or uniform_int_distribution<T>.
So shouldn't decltype fail trying to call an incomplete function? Or does decltype just not call the function at all and instead just evaluate the return type?
decltype is an unevaluated context. It just operates on the type level, so knowing that the body of dist, wherever it might be, returns some type X is enough.
decltype specifier inspects the declared type of an entity or queries the return type of an expression, thats what it does.
here an alternative usage. for showing that decltype cares only for type of any passing expression
template<typename U >
static typename std::enable_if<std::is_same<U, int>::value, std::uniform_int_distribution <U>>::type
dist(); // NOTE: no function body
template<typename U >
static typename std::enable_if<std::is_same<U, double>::value, std::uniform_real_distribution <U>>::type
dist()
{
//return; // NOTE: no return
}
decltype(dist<T>()) mUniformDistribution;
probably the Question why compiler does complain about dist() function as incomplete? i have no idea why is that.
demo example
template<typename T >
class Random
{
public:
Random(const T& min, const T& max)
: mUniformDistribution(min, max)
{}
T operator()()
{
return mUniformDistribution(mEngine);
}
private:
std::default_random_engine mEngine{ std::random_device()() };
template<typename U >
static typename std::enable_if<std::is_same<U, int>::value, std::uniform_int_distribution <U>>::type
dist(); // NOTE: no function body
template<typename U >
static typename std::enable_if<std::is_same<U, double>::value, std::uniform_real_distribution <U>>::type
dist()
{
//return; // NOTE: no return
}
decltype(dist<T>()) mUniformDistribution;
};
int main()
{
Random<int> getRandom(0, 9);
for (int i = 0; i<9; ++i)
std::cout << getRandom() << '\n';
}