bhache
bhache

Reputation: 39

Issue with using std::conditional in a template class

I am trying to define a template class with non-type template parameter. I then determine the type of the member of this class from this template parameter using std::conditional. I am running into some compile time errors.

Below is the MWE. In this MWE, I am trying to make the member, FiniteElementList, of template class, Mesh, either std::vector or std::list based on the condition being true (structured) or false (unstructured) respectively. The class, Mesh, contains some member and friend functions that are only declared for the moment and might be defined at a later stage.

MWE

// c++ standard library headers                                                                                                                                                  
#include <list>         // std::list                                               
#include <type_traits>  // std::conditional                                        
#include <vector>       // std::vector                                             
                                                                                     
// (un)structured flag                                                             
enum class StructuredUnstructured { kStructured = true, kUnstructured = false };
                                                                                     
// (un)structured mesh                                                             
template <StructuredUnstructured is_structured>                                    
class Mesh {                                                                       
  using Element = double;                                                          
  using FiniteElementList =                                                        
      typename std::conditional<is_structured ==                                   
                                    StructuredUnstructured::kStructured,           
                                std::vector<Element>, std::list<Element>>::type;
                                                                                     
 public:                                                                           
  // constructors                                                                  
  Mesh() = default;                                                                
  Mesh(const Mesh& m) = default;      // copy constructor                          
  Mesh(Mesh&& m) noexcept = default;  // move constructor                          
                                                                                     
  // assignment operators                                                          
  Mesh& operator=(const Mesh& m) = default;      // copy assignment                
  Mesh& operator=(Mesh&& m) noexcept = default;  // move assignment                
                                                                                     
  ~Mesh() = default;                                                               
                                                                                     
  void Indexing() {}                                                               
                                                                                     
  void Properties() {}                                                             
                                                                                     
  friend Mesh ReadMesh() {}                                                        
                                                                                     
  friend void WriteMesh() {}                                                       
                                                                                     
  friend void WriteMeshVTK() {}                                                    
                                                                                     
  private:                                                                          
    FiniteElementList finite_elements_{};                                            
};                                                                                 
                                                                                     
int main() {                                                                       
  Mesh<StructuredUnstructured::kUnstructured> unstructured;                        
  Mesh<StructuredUnstructured::kStructured> structured;
}

Compilation:

I am trying to compile with clang compiler as follows

c++ -O3 -Wall -Wextra -Wpedantic -std=c++17 mesh_structure.cpp -o mesh_structure

And I run into the following errors

mesh_structure.cpp:34:15: error: functions that differ only in their return type cannot be overloaded
  friend Mesh ReadMesh() {}
         ~~~~ ^
mesh_structure.cpp:46:45: note: in instantiation of template class 'Mesh<StructuredUnstructured::kStructured>' requested here
  Mesh<StructuredUnstructured::kStructured> structured;
                                            ^
mesh_structure.cpp:34:15: note: previous declaration is here
  friend Mesh ReadMesh() {}
         ~~~~ ^
mesh_structure.cpp:36:15: error: redefinition of 'WriteMesh'
  friend void WriteMesh() {}
              ^
mesh_structure.cpp:36:15: note: previous definition is here
mesh_structure.cpp:38:15: error: redefinition of 'WriteMeshVTK'
  friend void WriteMeshVTK() {}
              ^
mesh_structure.cpp:38:15: note: previous definition is here
3 errors generated.

Strange enough, the code compiles if I remove the second line, Mesh<StructuredUnstructured::kStructured> structured;, from main() function. I don't quite get what went wrong here. Any help would be greatly appreciated.

Thank you.

Upvotes: 1

Views: 266

Answers (1)

463035818_is_not_an_ai
463035818_is_not_an_ai

Reputation: 122585

The issue is not with std::conditional. Lets first look at a much simpler case with same problem:

template <bool>
struct foo {
    friend void bar () {};
};

int main()
{
    foo<true> x;
    foo<false> y;
}

bar is defined twice, hence the error:

<source>: In instantiation of 'struct foo<false>':
<source>:9:16:   required from here
<source>:3:17: error: redefinition of 'void bar()'
    3 |     friend void bar () {};
      |                 ^~~
<source>:3:17: note: 'void bar()' previously declared here

To define it once, move the definition out of the templates definition:

template <bool>
struct foo {
    friend void bar ();
};

void bar() {}

int main()
{
    foo<true> x;
    foo<false> y;
}

Next, another issue is that you are trying to declare a Mesh<kStructured> ReadMesh() and a Mesh<kUnstructured> ReadMesh() (within the template definition the name Mesh refers to Mesh<is_structured>, except some special cases).

Thats not going to work unless you make ReadMesh a function template. You cannot overload based on return value alone. Once ReadMesh is a template, you can befriend only the matching specialization like this:

template <bool> struct foo;

template <bool x> foo<x> bar() {}

template <bool x>
struct foo {
    friend foo bar<x> ();
};

int main()
{
    foo<true> x;
    foo<false> y;
}

Upvotes: 4

Related Questions