Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to define a specialized class method outside of class body in C++?

I have a template class A<T> and its specialization for integral arguments. And both the class and its specialization declare the method foo(), which I would like to define outside of class bodies:

#include <concepts>

template<class T> 
struct A { static void foo(); };

template<std::integral T>
struct A<T> { static void foo(); };

template<class T>
void A<T>::foo() {}

template<std::integral T>
void A<T>::foo() {}
 
int main() { A<int>::foo(); }

GCC accepts this code.

Clang prints the error https://gcc.godbolt.org/z/hYfYGPfMh :

error: type constraint differs in template redeclaration
template<std::integral T>

And MSVC prints errors on both method definitions:

error C3855: 'A<T>': template parameter 'T' is incompatible with the declaration
error C2447: '{': missing function header (old-style formal list?)
error C2065: 'foo': undeclared identifier

Please suggest how to define methods outside class bodies and make all the compilers happy?

like image 348
Fedor Avatar asked Jul 30 '21 10:07

Fedor


People also ask

What is used to define class methods outside the class?

You can define a function outside of a class and then use it in the class body as a method: def func(self): print("func") class MyClass: myMethod = func. You can also add a function to a class after it has been defined: class MyClass: pass def func(self): print("func") MyClass.myMethod = func.

How would you define member function outside the class template?

Member functions of class templates (C++ only) You may define a template member function outside of its class template definition. The overloaded addition operator has been defined outside of class X . The statement a + 'z' is equivalent to a. operator+('z') .

How do you define a method outside a class in C++?

When you define a class method outside the class, you need to prefix the method name with the class name and scope resolution operator. The important part is not to use the scope access keywords, such as private , protected or public .

What are specialized classes in C++?

It is possible in C++ to get a special behavior for a particular data type. This is called template specialization. Template allows us to define generic classes and generic functions and thus provide support for generic programming.

How to define a class method outside the class definition in C++?

In the below program, we are creating a C++ program to define a class method outside the class definition. See the above code, there are three methods printText1 (), printText2 () and printValue () which are declared inside the class definitions and methods are defining outside the class definition using Scope Resolution Operator (::).

What is a method in a class?

A Method in a Class is a function that operates inside or outside of this belonging class. Methods are defined in a class in two ways, Inside of the class or at the outside of the class. Let’s see them, 1. Defining a Method Inside a Class In this method, we will declare and define a method inside the class.

Can a function be used as a method of a class?

Yes. You can define a function outside of a class and then use it in the class body as a method: You can also add a function to a class after it has been defined:

How do you define a function outside of a class?

To define a function outside the class definition, you have to declare it inside the class and then define it outside of the class. This is done by specifiying the name of the class, followed the scope resolution :: operator, followed by the name of the function:


2 Answers

I'm pretty sure that both the MS and Clang compilers are in error here and GCC is compiling your code correctly. Until these bugs are fixed in the other compilers, I suggest to continue using the concept pattern instead of going back to outdated methods. Simply work around the bug using an additional class:

#include <concepts>
#include <iostream>

// This is a work-around for using concept specialization of
// classes in conjunction with out-of-body definition of members.
// Only helpful for MSVC and Clang. GCC is properly compiling
// out-of-body concept specializations.

template <typename T>
class A
{
    // For MSVC ONLY: the default template seems require being empty
    // for this to work, but do fiddle around with it.
    
    // (Also works with MSVC:)
    A()                         = delete;
    A(const A&)                 = delete;
    A(A&&) noexcept             = delete;
    A& operator =(const A&)     = delete;
    A& operator =(A&&) noexcept = delete;
    ~A()                        = delete;

    // Clang and GCC can have members just fine:
    // static const char* foo();
};

// We use distinct base classes to define our concept specializations of A.
template <std::signed_integral T>
class A_Signed_Integral
{
public:
    static const char* foo();
};
template <std::unsigned_integral T>
class A_Unsigned_Integral
{
public:
    static const char* foo();
};

// And then we wrap them using the actual concept specializations of A,
// making the specializations effectivel the same class as the base class.
template <std::signed_integral T>
class A<T> :
    public A_Signed_Integral<T>
{
public:
    using A_Signed_Integral<T>::A_Signed_Integral;  // grab all ctors
    using A_Signed_Integral<T>::operator =;         // an exceptional case
};
template <std::unsigned_integral T>
class A<T> :
    public A_Unsigned_Integral<T>
{
public:
    using A_Unsigned_Integral<T>::A_Unsigned_Integral;
    using A_Unsigned_Integral<T>::operator =;
};

// Out-of-body definitions can be located to another file
template <std::signed_integral T>
inline const char* A_Signed_Integral<T>::foo()
{
    return "using A<std::signed_integral T> foo";
}

template <std::unsigned_integral T>
inline const char* A_Unsigned_Integral<T>::foo()
{
    return "using A<std::unsigned_integral T> foo";
}

int main()
{
    std::cout << A<signed long>::foo() << std::endl;
    std::cout << A<unsigned long>::foo() << std::endl;

    return 0;
}

(Tested with all three compilers and works fine: see gcc.godbolt.org)

In the future, once the bugs are fixed it should be relatively easy with search and replace to erase the base classes in favor of just using the concept specializations of A.

EDIT: Updating the example to work for MSVC, which cannot seem to make use of the default template yet.

like image 82
doticu Avatar answered Oct 11 '22 08:10

doticu


I am not a C++ template expert, I tried something like below

template<class T, bool = std::is_integral_v<T>>
struct A
{};

template<class T>
struct A<T, false>
{ 
   static void foo(); 
};

template<class T>
struct A<T, true> 
{ 
   static void foo(); 
};

template<class T>
void A<T,false>::foo() 
{
  std::cout << "I am working on a non-integral type" << std::endl;
}

template<class T>
void A<T, true>::foo() 
{
  std::cout << "I am working on an integral type" << std::endl;
}

int main()
{
  A<int>::foo();
  A<float> ::foo();

  return 0;
}

and the code gave me the results on MS C++ compiler

I am working on an integral type
I am working on a non-integral type
like image 31
Nitheesh George Avatar answered Oct 11 '22 09:10

Nitheesh George