0

I have a template function, which return a vector of data. Now I try to use it with std::complex, but I need a explicit template specialization version of it.

#include <vector>
#include <complex>

template<typename T>
std::vector<T> FillData(unsigned int n)
{
    std::vector<T> v;
    return v;
}

template<>
std::vector<std::complex<double>> FillData(unsigned int n)
{
    std::vector<std::complex<double>> v;
    return v;
}

int main(int argc, char* argv[])
{
auto v1 = FillData<float>(10);
auto v2 = FillData<int>(10);
auto v3 = FillData<std::complex<double>>(10);
auto v4 = FillData<std::complex<float>>(10);  // call the one not expected
}

My question is, how can I specialize the template with different type of std::complex? Do I need one explicit specialization for each type?

Thanks in advance.

Tiger Hwang
  • 300
  • 1
  • 5
  • 16
  • 3
    You either need an explicit instantiation for each type, or you need to use SFINAE to detect that `T` is actually a `std::complex`, or you need to use structs so you can do partial specialization. Functions cannot be partially specialized – Justin Sep 15 '17 at 21:03
  • This is for sure a dupe... – Nir Friedman Sep 15 '17 at 21:10

2 Answers2

1

Forward to a class that you can partial specialize:

template <typename T>
struct FillDataHelper
{
    std::vector<T> operator () () const { return {}; }
};

template <typename T>
struct FillDataHelper<std::complex<T>>
{
    std::vector<std::complex<T>> operator () () const { return {}; }
};

template<typename T>
std::vector<T> FillData(unsigned int n)
{
    return FillDataHelper<T>{}();
}
Jarod42
  • 203,559
  • 14
  • 181
  • 302
  • There are many functions doing different works. I would prefer the other override solution which need less change. Thanks. – Tiger Hwang Sep 15 '17 at 21:56
  • @TigerHwang If you have a lot of cases, that’s an argument for this solution. If you ever need to do something different for a non-template type, this method will work but the other one won’t. – Daniel H Sep 15 '17 at 22:10
1

pass a model of the vector you want filled by value, and then let RVO the as-if rule return it back to you with almost no overhead.

You can now overload the FillData function if you need different behaviour depending on T.

#include <vector>
#include <complex>

template<typename T>
std::vector<T> FillData(std::vector<T> v, unsigned int n)
{
    v.reserve(n);
    for(int i = 0 ; i < n ; ++n)
    {
        // for example
        v.push_back(0);
    }
    return v;
}

template<typename T>
std::vector<std::complex<T>> FillData(std::vector<std::complex<T>> v, unsigned int n)
{
    v.reserve(n);
    for(int i = 0 ; i < n ; ++n)
    {
        // for example
        v.push_back(std::complex<T>(1, 0));
    }
    return v;
}

int main(int argc, char* argv[])
{
    auto v1 = FillData(std::vector<float>(), 10);
    auto v2 = FillData(std::vector<int>(), 10);
    auto v3 = FillData(std::vector<std::complex<double>>(), 10);
    auto v4 = FillData(std::vector<std::complex<float>>(), 10);
}
Richard Hodges
  • 68,278
  • 7
  • 90
  • 142
  • 1
    Better to pass a tag than a dummy instance: `template struct tag {}; std::vector FillData(tag, unsigned int n);`. – Jarod42 Sep 15 '17 at 21:21
  • @Jarod42 passing a model has no overhead and allows for the fact that the model might already have data in it. – Richard Hodges Sep 15 '17 at 21:23
  • RVO doesn't work here because v is an argument. – juanchopanza Sep 15 '17 at 21:24
  • @juanchopanza struggling to see any performance loss here: https://godbolt.org/g/whGiA4 – Richard Hodges Sep 15 '17 at 21:32
  • I just said RVO doesn't apply. "as if" still does, and also v can be moved. – juanchopanza Sep 15 '17 at 21:34
  • This is nice. Let me try how to put tag into it. – Tiger Hwang Sep 15 '17 at 21:34
  • So this is actually overload of template, there are two templates, one for general type and one for std::complex<> type. It's not template specialization anymore. – Tiger Hwang Sep 15 '17 at 21:41
  • This is a bad idea if you have other specializations; overloading and specialization don’t mix well. – Daniel H Sep 15 '17 at 21:45
  • @TigerHwang one for std::vector, one for std::vector> - each one if declaring an infinite set of overloads, but the signatures of the two sets can never overlap. – Richard Hodges Sep 15 '17 at 21:45
  • @DanielH specialising template functions is never a good idea. Overloading or deferring to a specialised function object is always a superior solution. – Richard Hodges Sep 15 '17 at 21:46
  • @RichardHodges Why do you say that? Overloading doesn’t always work and I don’t see why deferring to a function object is superior to specialization. – Daniel H Sep 15 '17 at 21:48
  • @DanielH there's an informative piece on it here: http://www.gotw.ca/publications/mill17.htm – Richard Hodges Sep 15 '17 at 21:49
  • @RichardHodges So basically to allow you to do partial or complete specialization if you want. But again, that doesn’t show any problems with *just* specializations as long as you don’t have multiple template overloads; it seems to agree with my statement that they don’t mix, but its points don’t apply if you just specialize. Forwarding to a class works in all cases, but if you don’t do that either strategy seems to work if you don’t mix them in the wrong way. – Daniel H Sep 15 '17 at 22:05