iouvxz iouvxz - 15 days ago 4
C++ Question

C++ How to specialize a template using vector<T>?

Basicly ,I want to make a function behave differently for a vector(type) parameter and a non-vector type parameter .

#include <vector>
using namespace std;

template <typename type>
struct is_vector {
static const bool value = false;
};

template <typename type>
struct is_vector<vector<type>>
{
static const bool value = true;
};
template <typename type>
type read()
{
if (is_vector<type>::value)
{
type vec(10);
vec.front()=1;//left of '.front' must have class/struct/union
return vec;
}
else
{
return{};
}
}
int main()
{
auto i= read<int>();
}


I want to return a vector while using vector as the typename ,return an int while using int as the typename .

But since is_vector(int)::value returns false ,why would my compiler reports "left of '.front' must have class/struct/union" ?How can I make it work ?

What I want to achieve is to correctly deserialize a string to a vector(type) or a vector(vector(type)) .

I need to recursively call the read function ,while passing a multidemonsional vector as a template parameter ,but the compiler forbids me to do it .

template <typename type>
struct is_vector {
static const bool value = false;
};

template <typename type>
struct is_vector<vector<type>>
{
static const bool value = true;
};

template <typename type>
type read(char*& str)
{
if (is_vector<type>::value)
{
type vec(read<uint8_t>(str));
for (auto& e : vec)
e = read<type::value_type>(str);
return vec;
}
return *reinterpret_cast<type*>((str += sizeof(type)) - sizeof(type));
}


So I tried specialization .

template<>
vector<int> read<vector<int>>(char*& str)
{
vector<int> vec(read<uint8_t>(str));
for (auto& e : vec)
e = read<int>(str);
return vec;
}//works

template <typename type>
template <>
vector<type> read<vector<type>>(char*& str)
{
vector<type> vec(read<uint8_t>(str));
for (auto& e : vec)
e = read<type>(str);
return vec;
}//don't work


Do I really need to manually rewrite my read function for every kind of types I use ?

(like vector(vector(vector(int)))?)

Answer

You want a function template foo<R> that is parameterized at least by return type R, and you want a specialized implementation when R = std::vector<U>, for arbitrary type U.

It doesn't matter what the arguments of foo<R> may be, so for illustration we'll assume there aren't any. Here's how you do that:

Define a trait template as follows:

template<typename T>
struct is_vector
{
    static constexpr bool value = false;
};

template<template<typename...> class C, typename U>
struct is_vector<C<U>>
{
    static constexpr bool value = 
        std::is_same<C<U>,std::vector<U>>::value;
};

With this,

is_vector<T>::value

will be true at compiletime if and only if T = std::vector<U>, for some U.

Then define two overloads of foo<R>() on the following lines:

template <typename R>
std::enable_if_t<!is_vector<R>::value,R> foo()
{
    // Your non-vector implementation instead of...
    std::cout << 
        "In non-vector specialization of `foo<R>()`\n";
    return R();
}

template <typename R>
std::enable_if_t<is_vector<R>::value,R> foo()
{
    // Your vector implementation instead of...
    std::cout << 
        "In vector specialization of `foo<R>()`\n";
    return R();
}

These two overloads are mutually exclusive and jointly exhaustive. The first overload pans out to be legal code if and only if is_vector<R>::value is false. The second overload pans out to be legal code if and only if is_vector<R>::value is true. That's thanks to the behaviour of std::enable_if, which you should study and understand.

When the compiler needs to pick one these template overloads to implement some call foo<type>() that it finds in your code, it discovers that exactly one of the overloads won't even compile when type is plugged in for the template parameter R. The first one won't compile if type is some std::vector<U> and the second one won't compile if type is not some std::vector<U>. Helpfully, the compiler picks the one that it can compile. That's called SFINAE ("Substitution Failure Is Not An Error"), and it's the solution of your problem.

Here's an illustrative program:

#include <vector>
#include <type_traits>
#include <iostream>

template<typename T>
struct is_vector
{
    static constexpr bool value = false;
};

template<template<typename...> class C, typename U>
struct is_vector<C<U>>
{
    static constexpr bool value = 
        std::is_same<C<U>,std::vector<U>>::value;
};

template <typename R>
std::enable_if_t<!is_vector<R>::value,R> foo()
{
    // Your non-vector implementation instead of...
    std::cout << 
        "In non-vector specialization of `foo<R>()`\n";
    return R();
}

template <typename R>
std::enable_if_t<is_vector<R>::value,R> foo()
{
    // Your vector implementation instead of...
    std::cout << 
        "In vector specialization of `foo<R>()`\n";
    return R();
}

int main()
{
    auto i = foo<int>();
    (void)i;
    auto vc = foo<std::vector<char>>();
    (void)vc;
    return 0;
}

which will output:

In non-vector specialization of `foo<R>()`
In vector specialization of `foo<R>()`

(gcc 6.1/clang 3.8, -std=c++14 see live)

Comments