admin管理员组

文章数量:1415673

My goal is simple: I want to specialize a method template for multiple types at once. I've googled around for possible ways to accomplish this task and found that concepts can be used for that.

I've created a simplified test program that resembles my target one to test how concepts work:

#include <iostream>
#include <concepts>

class A {
public:
    std::string getName() const { return "A"; }
};

class B {
public:
    std::string getName() const { return "B"; }
};

class C {
public:
    std::string getName() const { return "C"; }
};

class D {
public:
    std::string getName() const { return "D"; }
};

// Concept for classes C and D
template<typename T>
concept IsCorD = std::same_as<T, C> || std::same_as<T, D>;

// User class with the method template
class User {
public:
    // Primary template declaration
    template<typename T>
    void doSomething();
};


// Specialization for A
template<>
void User::doSomething<A>() {
    std::cout << "Specialized case for A" << std::endl;
}

// Specialization for B
template<>
void User::doSomething<B>() {
    std::cout << "Specialized case for B" << std::endl;
}

// Partial specialization for C and D using concepts
template<IsCorD T>
void User::doSomething<T>() {
    std::cout << "Case for C or D" << std::endl;
}

int main() {
    User user;
    user.doSomething<A>();
    user.doSomething<B>();
    user.doSomething<C>();
    user.doSomething<D>();

    return 0;
}

Here I'm trying to create a method template specialization for both C and D classes using the IsCorD concept. I use the following command to compile it:

g++ main.cpp --std=c++20 -Wall -Wextra -Wpedantic -O3 -o main

And I get the following compilation error:

main.cpp:51:27: error: non-class, non-variable partial specialization ‘doSomething<T>’ is not allowed
   51 | void User::doSomething<T>() {
      |                           ^
main.cpp:51:6: error: no declaration matches ‘void User::doSomething()’
   51 | void User::doSomething<T>() {
      |      ^~~~
main.cpp:33:10: note: candidate is: ‘template<class T> void User::doSomething()’
   33 |     void doSomething();
      |          ^~~~~~~~~~~
main.cpp:29:7: note: ‘class User’ defined here
   29 | class User {
      |       ^~~~

What am I doing wrong? How to get it compiled and working as intended? The expected output is:

Specialized case for A
Specialized case for B
Case for C or D
Case for C or D

Community members have suggested that this question maybe a duplicate of Why can I seemingly define a partial specialization for function templates?. I checked answers to that question, and they suggest to use std::enable_if to work around the forbidden partial specialization. But the answers there don't provide a solution using concepts.

My goal is simple: I want to specialize a method template for multiple types at once. I've googled around for possible ways to accomplish this task and found that concepts can be used for that.

I've created a simplified test program that resembles my target one to test how concepts work:

#include <iostream>
#include <concepts>

class A {
public:
    std::string getName() const { return "A"; }
};

class B {
public:
    std::string getName() const { return "B"; }
};

class C {
public:
    std::string getName() const { return "C"; }
};

class D {
public:
    std::string getName() const { return "D"; }
};

// Concept for classes C and D
template<typename T>
concept IsCorD = std::same_as<T, C> || std::same_as<T, D>;

// User class with the method template
class User {
public:
    // Primary template declaration
    template<typename T>
    void doSomething();
};


// Specialization for A
template<>
void User::doSomething<A>() {
    std::cout << "Specialized case for A" << std::endl;
}

// Specialization for B
template<>
void User::doSomething<B>() {
    std::cout << "Specialized case for B" << std::endl;
}

// Partial specialization for C and D using concepts
template<IsCorD T>
void User::doSomething<T>() {
    std::cout << "Case for C or D" << std::endl;
}

int main() {
    User user;
    user.doSomething<A>();
    user.doSomething<B>();
    user.doSomething<C>();
    user.doSomething<D>();

    return 0;
}

Here I'm trying to create a method template specialization for both C and D classes using the IsCorD concept. I use the following command to compile it:

g++ main.cpp --std=c++20 -Wall -Wextra -Wpedantic -O3 -o main

And I get the following compilation error:

main.cpp:51:27: error: non-class, non-variable partial specialization ‘doSomething<T>’ is not allowed
   51 | void User::doSomething<T>() {
      |                           ^
main.cpp:51:6: error: no declaration matches ‘void User::doSomething()’
   51 | void User::doSomething<T>() {
      |      ^~~~
main.cpp:33:10: note: candidate is: ‘template<class T> void User::doSomething()’
   33 |     void doSomething();
      |          ^~~~~~~~~~~
main.cpp:29:7: note: ‘class User’ defined here
   29 | class User {
      |       ^~~~

What am I doing wrong? How to get it compiled and working as intended? The expected output is:

Specialized case for A
Specialized case for B
Case for C or D
Case for C or D

Community members have suggested that this question maybe a duplicate of Why can I seemingly define a partial specialization for function templates?. I checked answers to that question, and they suggest to use std::enable_if to work around the forbidden partial specialization. But the answers there don't provide a solution using concepts.

Share Improve this question edited Feb 11 at 12:20 Ruslan asked Feb 11 at 11:10 RuslanRuslan 871 silver badge7 bronze badges 4
  • 1 This question is similar to: Why can I seemingly define a partial specialization for function templates?. If you believe it’s different, please edit the question, make it clear how it’s different and/or how the answers on that question are not helpful for your problem. – Oersted Commented Feb 11 at 11:26
  • @Oersted I don't think it is similar. This question does full specialisations and fails when partial specialisation is attempted. The question you linked did overloading and thought it's a partial specialisation. – Yksisarvinen Commented Feb 11 at 11:36
  • 1 In C++ partial specialization is supported only for class templates. And it does not look like you need any kind of specialization (nor concepts). Just perform checks in method else if constexpr (::std::is_same_v<T, C> or ::std::is_same_v<T, D>) {... – user7860670 Commented Feb 11 at 11:51
  • I do not see any reason to do template specialization in this code. This is important since if you could show why you think specialization is needed then we can provide best solution for that problem. Note that instead saying "for this type do something else", usually it is better to say: "if this class can do that then use this alternative". – Marek R Commented Feb 11 at 12:01
Add a comment  | 

1 Answer 1

Reset to default 4

The error occurs because C++ doesn't allow partial specialization of function templates (including member function templates). That means you cannot write a version of doSomething<T>() that is only enabled for types C and D by "partially specializing" the template. Instead of trying to specialize for types C and D partially, you can create separate overloads of your member functions and constrain them using concepts. Here's one thing that you can try to do:

#include <iostream>
#include <concepts>
#include <string>

// Definitions of classes A, B, C, and D.
class A {
public:
    std::string getName() const { return "A"; }
};

class B {
public:
    std::string getName() const { return "B"; }
};

class C {
public:
    std::string getName() const { return "C"; }
};

class D {
public:
    std::string getName() const { return "D"; }
};

// Concept that checks if a type is either C or D.
template<typename T>
concept IsCorD = std::same_as<T, C> || std::same_as<T, D>;

class User {
public:
    // Overload for A
    template<typename T>
    requires std::same_as<T, A>
    void doSomething() {
        std::cout << "Specialized case for A" << std::endl;
    }

    // Overload for B
    template<typename T>
    requires std::same_as<T, B>
    void doSomething() {
        std::cout << "Specialized case for B" << std::endl;
    }

    // Overload for C and D
    template<typename T>
    requires IsCorD<T>
    void doSomething() {
        std::cout << "Case for C or D" << std::endl;
    }
};

int main() {
    User user;
    user.doSomething<A>(); // Prints: Specialized case for A
    user.doSomething<B>(); // Prints: Specialized case for B
    user.doSomething<C>(); // Prints: Case for C or D
    user.doSomething<D>(); // Prints: Case for C or D
    return 0;
}

Instead, define multiple overloaded versions of doSomething that are constrained using the requires clause and concepts:

One overload for type A (using requires std::same_as<T, A>).
One overload for type B.
One overload for types C or D (using the IsCorD concept).

So the output should look like this:

Specialized case for A
Specialized case for B
Case for C or D
Case for C or D

Hope this helps!

本文标签: cHow to specialize a method template using conceptsStack Overflow