Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

template class method specialization compilation error

I'm making my own test framework in c++ in order to learn more of this language (and wow how much I learned), but I've just hit a critical point -> I try to specialize my contains check to fail directly when it's not a container and else do the real check.

For now my code looks like this:

#include <iostream>
#include <vector>

template<typename ActualType> class AssertionMatcher final {
  public:
    explicit AssertionMatcher(const ActualType &actual): _actual(std::move(actual)) {}

    template<typename ContainedType> auto contains(const ContainedType &contained) const -> void {
        std::cout << "fail";
    }

  private:
    const ActualType _actual;
};

template<typename ContainedType>
class AssertionMatcher<std::vector<ContainedType>> {
  public:
    auto contains(const ContainedType &contained) const -> void {
        std::cout << "ok";
    }
};

template<typename ActualType> [[nodiscard]] auto assertThat(const ActualType &actual) -> AssertionMatcher<ActualType> {
    return AssertionMatcher(actual);
}

And I call it this way:

int main() {
    const std::vector container({1, 2, 3});
    assertThat(container).contains(2);
}

You can find the live example here: https://godbolt.org/z/fxG7a7Tvz

I've looked at many others questions here to find how to achieve that + this page (https://en.cppreference.com/w/cpp/language/template_specialization.html). But this code won't compile (gcc 14 from online compiler):

<source>: In instantiation of 'AssertionMatcher<ActualType> assertThat(const ActualType&) [with ActualType = std::vector<int, std::allocator<int> >]':
<source>:30:22:   required from here
   30 |     (void) assertThat(container);
      |            ~~~~~~~~~~^~~~~~~~~~~
<source>:25:12: error: too many initializers for 'AssertionMatcher<std::vector<int, std::allocator<int> > >'
   25 |     return AssertionMatcher(actual);
      |            ^~~~~~~~~~~~~~~~~~~~~~~~

I have run out of ideas how to fix that. Do you have any ideas?

like image 709
Gashmob Avatar asked Jun 27 '26 01:06

Gashmob


1 Answers

in (void) assertThat(container); container is a std::vector<int> so in assertThat the form return AssertionMatcher(actual) uses the class template specialization template<typename ContainedType> class AssertionMatcher<std::vector<ContainedType>>, because you don't define any constructor for it there is only the default constructor whose does not have argument, but you give the argument actual.

Adding in the class template specialization for instance :

explicit AssertionMatcher(const std::vector<ContainedType> &actual) {
  // does something with actual
}

and the program compile.

In main doing (void) assertThat(container).contains(2); rather than (void) assertThat(container); writes ok


Of course currently assertThat(container).contains(4) also writes ok, you probably want something like

template<typename ContainedType>
class AssertionMatcher<std::vector<ContainedType>> {
  public:
    explicit AssertionMatcher(const std::vector<ContainedType> &actual)
      : _actual(std::move(actual)) {}
    auto contains(const ContainedType &contained) const -> void {
        std::cout << ((std::find(_actual.begin(), _actual.end(), contained)
                       == _actual.end())
                      ? "nok" : "ok")
                  << std::endl;
    }

  private:
    const std::vector<ContainedType> _actual;
};

and now

int main() {
  const std::vector container({1, 2, 3});
   
  assertThat(container).contains(2);
  assertThat(container).contains(4);
  assertThat(123).contains(2);
  return 0;
}

writes

ok
nok
fail
like image 112
bruno Avatar answered Jun 29 '26 13:06

bruno