3

I'm currently implementing a dataset helper class template storing floating point values (Scalar) in a dynamically sized Eigen::Matrix constructed from a vector of different values types (Element) additionally storing a reference to this input vector. Now i want to partially specialize the constructor in the vector value type remaining a template in the scalar type to be explicitly instantiated.

Unfortunately i'm getting "unable to match function definition to an existing declaration" on VS 2010. The code is as simple as:

template <class Scalar, class Element> struct DataSet
{
    DataSet(std::vector<Element> const & source);

    // several generic member functions here ...

    Eigen::Matrix<Scalar, ...  > data;
    std::vector<Element> const & source;
};

template<class Scalar>
DataSet<Scalar, SomeClass>::DataSet(std::vector<SomeClass> const & input)
{
    // special impl for Element==SomeClass ...
}

SomeClass should be automatically be figured out by the compiler, when done right but i tried all meaningful combinations but still getting:

*.cpp(79) C2244 : unable to match function definition to an existing declaration
see declaration of 'DataSet<Scalar, Element>::DataSet'

I was not able to find a matching example by searching the internet yet. Thanks in advance!

EDIT:

To make it more specific, in my real world case i want to be able to define several partial specializations to the constructor with different types for Element e.g:

template<Scalar>
DataSet<Scalar, FirstClass>::DataSet(std::vector<FirstClass> const & first)
: data()
, source(first)
{
    // special impl here ...
}

template<Scalar>
DataSet<Scalar, std::shared_ptr<SecondClass> >::DataSet(std::vector<std::shared_ptr<SecondClass> > const & second)
: data()
, source(second)
{
    // special impl here ...
}

Redeclaring/specializing the class completely to a certain typename is not desired. Then there is little use to be a template at all. I want the solution as it is, otherwise there might be other strategies to my problem.

FIN:

Since it looks like not being possible to share the type Element between class template and constructor by only specializing the constructor (which is somehow related to an implicit specialization of the class) i removed the reference source from the class template entirely and copied the needed information into a generic container and implemented the constructors via overloads.

daku
  • 43
  • 6

2 Answers2

3

When defining your constructor, you didn't explicitly provide both template arguments for its class. That would need to be revised as follows:

template<typename T_Scalar, typename T_Element>
DataSet<T_Scalar, T_Element>                    // template args for type
::DataSet(std::vector<T_Element> const &input)  // but none for constructor
{
    // stuff
}

Tangentially related: Unlike methods, template arguments for classes cannot be deduced from constructor calls. That is: until C++17 comes around! woo!

The next stumbling block you faced is that template specialisations do not 'inherit' members from their primary template. It is somewhat intuitive to assume they would, but it's just not so. Until I find an official rationale, I presume it's because template arguments might make certain members totally inapplicable to a specialisation, rendering implicit 'inheritance' problematic. If so, it would've been decided to require full redeclaration / not judged worthwhile to add arcane syntax to specify which primary 'base' members are 'inherited'... when you can simply use real inheritance to ensure they are.

Anyway, what that means is that to get a partial specialisation, you need to declare the whole thing - in this case, the class and its constructor - before you can specialise that constructor's definition. You hadn't declared these ahead of time, so the compiler rightly complained that it couldn't see a declaration.

// Define specialised class
template<typename T_Scalar>
class DataSet<T_Scalar, SomeClass>
{
public:
    // Declare its ctor
    DataSet(std::vector<SomeClass> const &);
}

// Implement its ctor
template<typename T_Scalar>
DataSet<T_Scalar, SomeClass> // complete template args
::DataSet(std::vector<SomeClass> const &input)
{
    // stuff
}

See my working example of an equivalent template class, showing general vs. specialised instantiations.

To add to your original confusion, which is fair! - note that out-of-line definitions can get very complicated indeed if a template class itself contains a template function, because then you need 2 template clauses, e.g.

template<typename TA, typename TB>
class Widget {
    template<typename TC>
    void accept_gadget(TC &&gadget);
};

template<typename TA, typename TB>
template<typename TC>
Widget<TA, TB>
::accept_gadget(TC &&gadget)
{
    /* ... */
}

Something that will help a lot in many contexts, especially including such out-of-line template definitions, is if the proposal to allow namespace class is accepted in a future version. Very sad this didn't make it into C++17... and very odd that it was ever missing in the 1st place!

Community
  • 1
  • 1
underscore_d
  • 6,309
  • 3
  • 38
  • 64
  • Ok, i was somehow in fear, that i am expecting somewhat that is not supported for c++ templates yet (since i found no matching examples) but i should be able to make it work somehow, ain't i? I will most probably need a reference to the input vector later on, that's why `Element` should remain a template argument to the class, not the constructor ... any suggestions? – daku Jul 14 '16 at 13:02
  • @daku yes, it is a template argument, so just include it as one! see my edit – underscore_d Jul 14 '16 at 13:04
  • Wasn't the purpose to get a partial specialization with `Element`=`SomeClass` different from the general form? – O'Neil Jul 14 '16 at 13:50
  • @O'Neil: Yes it was, i was just happy to get over the mentioned compiler error but now have the problem with a second specialization since as understood initially wrong the given solution is NOT the expected specialization but again the templates default implementation with SomeClass instead Element as an arbitrary template argument name, i will try out the provided solution but i suspect the double template<...> to be incorrect since there are no nested templates, keep me updates on ideas, i'll reply when i finally made it ... – daku Jul 14 '16 at 14:21
  • @daku I have looked into that for you and think you can do it. The only thing that caught me out was it seems we must redeclare the constructor when declaring the specialisation. Please see the new 2nd example in the answer and the equivalent demo that I used to puzzle through this on Coliru. :) – underscore_d Jul 14 '16 at 14:24
  • @underscore_d: This way it is a specialization of the complete class and i would loose the generic member functions from the initial template. I'm really interested in the solution since i initially thought it is just something about syntax but i am more and more getting the impression it cannot be done this way. I should probably remove the reference to the source argument, store the needed info in another container and make the constructor a templated functions in the input vector type. – daku Jul 14 '16 at 14:45
  • @daku True, `Members of partial specializations are not related to the members of the primary template.`- http://en.cppreference.com/w/cpp/language/partial_specialization You can probably tell that I don't specialise templates very often. :-) About your desire for reusing stuff from the primary template, have you considered inheriting from a template and reusing members that way? This might work for at least some cases. You could probably combine it with the CRTP and get some pretty complex relationships going. – underscore_d Jul 14 '16 at 14:49
  • @underscore_d: Of course that would be a solution but in fact it is just a helper class in a detail namespace. I wanted it to be as smart and as short as possible. Once starting to get to blown i will move away from it anyway – daku Jul 14 '16 at 14:55
  • @daku Unfortunately I personally can't think of a snappier way to do this. If you or anyone else figure out a way that you like better, please let us know! – underscore_d Jul 14 '16 at 14:57
  • @underscore_d See my answer. – O'Neil Jul 14 '16 at 15:18
  • fwiw, the accepted wisdom does indeed appear to be that inheritance and possibly CRTP are the way to go, e.g. http://stackoverflow.com/questions/4955609/how-to-specialize-only-some-members-of-a-template-class and http://stackoverflow.com/questions/18617791/partial-specialization-use-the-primary-template-members – underscore_d Jul 14 '16 at 15:56
2

According to §14.7.3.16:

In an explicit specialization declaration for a member of a class template or a member template that appears in namespace scope, the member template and some of its enclosing class templates may remain unspecialized, except that the declaration shall not explicitly specialize a class member template if its enclosing class templates are not explicitly specialized as well.

Still, you can use std::enable_if to partial-specialize your contructor:

template <class Scalar, class Element> struct DataSet
{
    template <class T>
    DataSet(std::vector<T> const & input, std::enable_if_t<!std::is_same<T, SomeClass>{}> * = nullptr) {
        std::cout << "Element\n";
    }
    template <class T>
    DataSet(std::vector<T> const & input, std::enable_if_t<std::is_same<T, SomeClass>{}> * = nullptr) {
        std::cout << "SomeClass\n";
    }
};

But this way is restrictive:

  • all your conditions must be exclusives
  • you'll have to modify the code of your class for every new class you want to handle.

Instead, I'd advise you to use a template helper structure:

DataSet(std::vector<Element> const & input) {
    Helper<Element>::do_it(input);
}

that you can specialize as you want:

template <class Element>
struct Helper {
    static void do_it(std::vector<Element> const & input) {
        std::cout << "General form with Element\n";
    }
};


template<>
struct Helper<SomeClass> {
    static void do_it(std::vector<SomeClass> const & input) {
        std::cout << "SomeClass\n";
    }
};

template<>
struct Helper<SomeOtherClass> {
    static void do_it(std::vector<SomeOtherClass> const & input) {
        std::cout << "SomeOtherClass\n";
    }
};

...
O'Neil
  • 3,790
  • 4
  • 16
  • 30
  • 1
    Thanks for your help but this will not make the reference to the source data possible directly and increases lines-of-code in contrast to my initial desire to keep it as simple as possible. I now removed this reference to the source data, and abstract the information to some generic container. The constructors can now be implemented as overloads without `Element`. But i am still curios where the problem is because in general the way is started should be the way to go from a syntactical point of view. – daku Jul 14 '16 at 15:47
  • @daku It is somewhat intuitive to assume specialised templates will 'inherit' members from the primary 'base', but it's just not the case. I _presume_ this is because template arguments might mean certain members are completely inapplicable in certain specialisations, making implicit 'inheritance' problematic. If so, it must've been decided to require full redeclaration & not judged worthwhile to add a lot of arcane syntax to specify whether or not members should be 'inherited'... when you can simply use _actual_ inheritance to ensure they are. But I'll see if I can find an on-record rationale – underscore_d Jul 14 '16 at 15:50
  • @underscore_d: That's because specialization is not inheritance ;-) Specializing the class template makes a complete new class. My goal was to specialize the constructor which obviously is not possible because `Element` is a template argument to the class template. It should be easily possible if i make the constructor a template function but then its probably not possible to share the argument `typename Element` between class and function template (for the ctor, or is it somehow?). – daku Jul 14 '16 at 16:01
  • @daku Yup, specialisation certainly isn't inheritance, hence all my 'air quotes' :-P By my understanding, your pessimistic scenario is correct: you can't template the ctor because you have nothing to template it _on_... given that you want to reuse a type already templated to the class itself. I'm not sure there's a way around this without either full redeclaration or inheritance - & if there is, it's certainly beyond my limited grasp of template acrobatics! Personally I _think_ I would use some blend of inheritance to resolve this, but I've not used this exact pattern, so I can't say for sure – underscore_d Jul 14 '16 at 16:12
  • You can pass your `data` member as parameter of do_it() if necessary. I have edited my answer to show you what you expected. But if you want your code to be evolutive, consider the second part. – O'Neil Jul 14 '16 at 16:28
  • First thanks for your effort, although it will require me some time to build up mojo to get it, at least for part 1). I was expecting some reference on why the intuitive syntax that im used to is not working to partially specialize the constructor in this way which you provided. – daku Jul 15 '16 at 06:48
  • Concerning part 2) this is probably the way to go if my problem would be that central to make the effort but it still misses storing the reference to the input data to be used in other member functions. For that the helper should store the reference and abstract away access to various `SomeClass`'es. The probably clearer solution i used now is traditional constructor overloading. What i'm still curios about is the `>{}>` part and what happens if the enable_if_t is not existing concerning `* = nullptr` – daku Jul 15 '16 at 06:55
  • `std::is_same<...>{}` = `std::is_same<...>::value`. And if the `enable_if`' condition is false, the constructor is discarded from overload resolution (see [SFINAE](http://en.cppreference.com/w/cpp/language/sfinae)). Not sure to understand what you want with data, but as I said, you can pass it as parameter (reference or not), or even return it from `do_it()` to achieve your goal. – O'Neil Jul 15 '16 at 13:00
  • @O'Neil: Thanks for your explanation, `{}` was new to me and the second part is exactly what i wanted to know without being hit by a complete specification on SFINAE (y). With 'data' i was referring to the source vector that i wanted to store within the `DataSet` (reference or not). I updated the example by initialization of member `source`... not just `do_it()` but `keep_it()`, the real reason for all that templating in `Element` – daku Jul 18 '16 at 13:40