'template specialization in header causes Linking issues

I am playing around with converting standard library functions like stoi to not throw exceptions on failures but keep running into linking issues. I followed steps from other similar questions but the linker is still not satisfied

Header.h

#include <optional>
#include <iostream>
#include <string>
#include <cstddef>
namespace temp{
    template<class T>
    std::optional<int> noexcept_stoi( const T& str, std::size_t* pos = nullptr, int base = 10)
    {
      //body of the function 
    }
    template<> inline std::optional<int> noexcept_stoi<std::wstring>( const std::wstring& str, std::size_t* pos, int base );
    template<> inline std::optional<int> noexcept_stoi<std::string>( const std::string& str, std::size_t* pos, int base );
};

Test.cpp

#include "Header.h"
int main(){
  std::string input = "123";
  auto result = temp::safe_stoi(input);
return 0;
}

Error: Undefined symbols for architecture arm64: "std::__1::optional temp::noexcept_stoi<std::__1::basic_string<char, std::__1::char_traits, std::__1::allocator > >(std::__1::basic_string<char, std::__1::char_traits, std::__1::allocator > const&, unsigned long*, int)"

What am I missing ?



Solution 1:[1]

The problem is that you have only provided the declarations for the specializations and not the corresponding definition. There are two ways to solve this as given below:

Method 1

Here we provide the corresponding definition for the specializations.

namespace temp{
    template<class T>
    std::optional<int> noexcept_stoi( const T& str, std::size_t* pos = nullptr, int base = 10)
    {
      return 4; //don't forget to return something
    }
    template<> inline std::optional<int> noexcept_stoi<std::wstring>( const std::wstring& str, std::size_t* pos, int base )
    {
        return 4;
    }
    template<> inline std::optional<int> noexcept_stoi<std::string>( const std::string& str, std::size_t* pos, int base )
    {
        return 4;
    }
};

Working demo

Method 2

We could make use of explicit template instantiation to solve this problem as shown below.

Header.h

#ifndef MYHEADER_H
#define MYHEADER_H
#include <optional>
#include <iostream>
#include <string>
#include <cstddef>
namespace temp{
    template<class T>
    std::optional<int> noexcept_stoi( const T& str, std::size_t* pos = nullptr, int base = 10)
    {
      return 4; //return something
    }
//-----------------v------------------------->no need for angle brackets as well as inline 
    extern template  std::optional<int> noexcept_stoi<std::wstring>( const std::wstring& str, std::size_t* pos, int base );
//-----------------v------------------------->no need for angle brackets as well as inline
    extern template  std::optional<int> noexcept_stoi<std::string>( const std::string& str, std::size_t* pos, int base );
};

#endif

main.cpp


#include <iostream>
#include "Header.h"
//explicit template instantiation definition
template  std::optional<int> temp::noexcept_stoi<std::wstring>( const std::wstring& str, std::size_t* pos, int base );
template  std::optional<int> temp::noexcept_stoi<std::string>( const std::string& str, std::size_t* pos, int base );
int main(){
  std::string input = "123";

  auto result = temp::noexcept_stoi(input);
return 0;
}

Working demo

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1