3

I have a fixed string implementation with a deduction guide like so:

// FixedString.hpp

template <size_t N>
struct FixedString
{
    char chars[N+1] = {};

    constexpr FixedString (const char (&str)[N+1])
    { 
        std::copy_n(str, N+1, chars);
    }
};

template <size_t N>
FixedString (const char (&str)[N]) -> FixedString<N-1>;

I also have a class that takes my FixedString type (with no defined size) as a template parameter:

// Foo.hpp

#include "FixedString.hpp"

template <FixedString TName>
class Foo
{
public:
    Foo ();
};

#include "Foo.impl.hpp"

... and a using declaration in some other file:

// OtherFile.hpp

using Bar_t = Foo<"bar">;

When I implement the constructor of Foo inside of Foo.hpp, everything works fine. But when I try to implement the constructor in a separate included header, like so:

// Foo.impl.hpp

#include "Foo.hpp"

template <FixedString TName>
Foo<TName>::Foo ()
{}

I get the following errors:

  • class template argument deduction failed
  • no matching function for call to 'FixedString(FixedString<...auto...>)'
  • conflicting declaration of template 'template<FixedString<...auto...> TName> int Foo()'

And the same goes for any method I try to implement outside of the main Foo class header.

So here is my question: is there anyway for me to separate the implementation from the declaration without specifying a hardcoded size for my fixed strings?

Am I forced into a single header approach here if I want my FixedString type to deduce the size automatically?

wohlstad
  • 12,661
  • 10
  • 26
  • 39
  • `FixedString` is a templated class. You may be looking for a `template template` parameter? – AndyG Aug 09 '22 at 02:11
  • @AndyG Aren't those for type parameters only? I'm trying to pass a non-type paramater. – Nicholas Bonjour Aug 09 '22 at 02:18
  • 1
    I'm not 100% sure what you're asking, are you looking for something like this? https://godbolt.org/z/GoTfd3bjv – AndyG Aug 09 '22 at 02:45
  • @AndyG Hm... That's exactly it... except it's seemingly not working on my machine. I'll try updating my compilers. Thanks! – Nicholas Bonjour Aug 09 '22 at 02:56
  • 1
    Yep, that was it. Weird considering I got *very* similar errors with both clang and mingw. – Nicholas Bonjour Aug 09 '22 at 03:11
  • @NicholasBonjour You have cyclic dependency in your program. For example, `Foo.hpp` includes `Foo.impl.hpp` and then `Foo.impl.hpp` includes `Foo.hpp`. Also see [C++ non-generic class in template](https://stackoverflow.com/questions/72963090/c-non-generic-class-in-template) where you'll see that from **C++20** onwards, we can use nontype parameter of class type. – Jason Aug 09 '22 at 04:19
  • @JasonLiam You misunderstood the question. I'm aware that non-type parameters are allowed in C++20. The problem was a compiler bug (or possibly just lack of support) not letting me use a specific set of non-type parameters that I was trying to use. Also, whether this is actually a cyclic dependency or not (I'm pretty sure it's not a problem since the include is at the **bottom** of `Foo.hpp`), this is a very common approach used to separate template implementations from their declarations: https://stackoverflow.com/questions/495021/why-can-templates-only-be-implemented-in-the-header-file – Nicholas Bonjour Aug 09 '22 at 23:53

1 Answers1

0

For posterity:

Turns out this is absolutely valid C++20 code (thanks @AndyG), but versions of clang lower than 12.0.0 and versions of gcc lower than 10.3 fail to compile.

Here's a link to an example where you can verify this (thanks again @AndyG): https://godbolt.org/z/GoTfd3bjv