Nkk
Nkk

Reputation: 53

C++ Concepts/SFINAE: clang and MSVC/G++ different results for out of line function definition with template and Concept/SFINAE

I am defining a constructors of a class that constrained to check for the type equivalence of the underlying type of passed-in iterator versus the node_type defined in my class, with the help of concepts or SFINAE.

However, tried a few combinations with is_same<>, is_same_v<>, enable_if<>, and same_as<> concept, the out-of-line definition will always yield compile time errors on at least one of the major compilers. Currently below snippet sees no error on MSVC/g++, but clang complains, all using latest version(19.28, 11.1, 12.0 respectively). Can anyone clarify what is wrong here? Thanks.

Edited:

As of GCC 12 and Clang 14, this bug persists.

#include <stdio.h>
#include <iterator>
#include <array>
#include <concepts>
#include <cstdlib>

template <typename FPType, std::size_t N>
class PointND {
private:
    std::array<FPType, N> coords_;
};

template <typename FPType, std::size_t N, typename ElemType>
class Tree {
public:

    struct node_type {
        PointND<FPType, N> key;
        ElemType value;
    };
    
    template <std::random_access_iterator RAI>
    requires std::same_as<typename std::iterator_traits<RAI>::value_type, 
                          typename Tree<FPType, N, ElemType>::node_type>
    Tree(RAI, RAI);
};

template <typename FPType, std::size_t N, typename ElemType>
template <std::random_access_iterator RAI> 
requires std::same_as<typename std::iterator_traits<RAI>::value_type, 
                      typename Tree<FPType, N, ElemType>::node_type>
Tree<FPType, N, ElemType>::Tree(RAI begin, RAI end) {
}

Error on this line::45:30: error: out-of-line definition of 'Tree<FPType, N, ElemType>' does not match any declaration in 'Tree<FPType, N, ElemType>'

Tree<FPType, N, ElemType>::Tree(RAI begin, RAI end) {

Upvotes: 1

Views: 372

Answers (1)

Jeff Garrett
Jeff Garrett

Reputation: 7528

On the C++11 example, add -fchecking and gcc will say:

<source>:52:51: internal compiler error: canonical types differ for identical types 'std::enable_if<(std::is_same<typename std::iterator_traits<_InputIterator>::iterator_category, std::random_access_iterator_tag>::value && std::is_same<typename std::iterator_traits<_InputIterator>::value_type, typename Tree<FPType, N, ElemType>::node_type>::value), int>' and 'std::enable_if<(std::is_same<typename std::iterator_traits<_InputIterator>::iterator_category, std::random_access_iterator_tag>::value && std::is_same<typename std::iterator_traits<_InputIterator>::value_type, Tree<FPType, N, ElemType>::node_type>::value), int>'
   52 | Tree<FPType, N, ElemType>::Tree(RAI begin, RAI end) {
      |                                                   ^
...
Please submit a full bug report,
with preprocessed source if appropriate.
Please include the complete backtrace with any bug report.
See <https://gcc.gnu.org/bugs/> for instructions.

gcc's failure to compile this is a compiler bug.

I suspect but cannot prove that clang's behavior on the C++20 code is also a bug.

There is an easy workaround. These bugs have to do with interpreting names, particularly a nested class, inside the context of an incomplete class. So... move the type to namespace scope:

Upvotes: 0

Related Questions