tmaric
tmaric

Reputation: 5497

Template parameters not deducible in partial specialization

I have a similar issue like the one found here but it might happen that I am still doing something different, so I will ask none-the less.

There are some types that will be tagged with a tag structure:

template<typename Geometry=void, typename Enable = void>
struct tag 
{
    typedef void type;
};

and point and triangle tags are introduced:

struct point_tag {}; 
struct triangle_tag {}; 

to construct the point type using the std::vector:

template<>
struct tag<std::vector<double>>
{
    typedef point_tag type; 
};

and a triangle type as an alias template of the std::array:

template<typename Point>
using triangle = 
typename std::enable_if
<
    std::is_base_of<typename tag<Point>::type, point_tag>::value,
    std::array<Point,3>
>::type;

that is enabled if the argument passed as Point parameter is really tagged with point_tag,

and afterwards, I would like to tag all triangles with the triangle_tag like this:

template <typename Point>
struct tag<triangle<Point>>  
{
    typedef triangle_tag type;
};

The std::array is aliased and not composited/inherited because composition and inheritance causes problems with the initializer list construction. However, the compililation fails with the error

g++ -std=c++1y main.cpp -o main 
main.cpp:31:8: error: template parameters not deducible in partial specialization:
 struct tag<triangle<Point>>  
        ^
main.cpp:31:8: note:         ‘Point’

If I don't rely on enabling the triangle based on the Point parameter being tagged, but do it for all types like this:

template<typename Point>
using triangle = 
// This works, but there is no restriction on Point to be tagged with point_tag.
std::array<Point, 3>;

then the compilation works fine. However, then triangle is also a triangle, and I am using function overloading based on arbitrary properties of types to reduce the function template set from those functions for which the enable_if fails. I am not relying on container interfaces for function templates to determine the viable template arguments because sometimes the implicit interfaces are exactly the same, but the operation semantics is different. For example, a triangle is a closed circular line segment (involves operation on 3 edges), and a point chain is an open-ended line segment (involves operations on 2 edges). All operations require a direct access operator which is the only requirement for the template parameter, which leads to ambiguity in function template instantiation when they are implemented without enable_if restrictions - all covered in the linked article.

Here is the complete example.

Is there something I'm missing? How to get around this issue?

Upvotes: 2

Views: 3801

Answers (2)

Jarod42
Jarod42

Reputation: 218323

What not use your Enable template parameter ?
Something like:

template <typename Point>
struct tag<
    std::array<Point, 3>,
    typename std::enable_if<
        std::is_base_of<
            typename tag<Point>::type,
            point_tag
        >::value
    >::type
>
{
    typedef triangle_tag type;
};

(ok, you repeat the enable_if...)

Live example

Upvotes: 1

Brandon Kohn
Brandon Kohn

Reputation: 1621

This works for me:

template <typename Point>
struct triangle
{
    static_assert(std::is_same<typename tag<Point>::type, point_tag>::value, "triangle can only contain elements which model a point_tag.");

    Point&       operator[](std::size_t i) { return elems[i]; }
    Point const& operator[](std::size_t i) const { return elems[i]; }    
    Point elems[3];
};

Upvotes: 1

Related Questions