2017-02-02 38 views
0

考慮這個簡單的連接函數實現。模板函數奇怪的鏈接器錯誤

#include <iostream> 
#include <string> 

namespace detail { 
    template<typename... Args> 
    size_t calcSize(const Args&... args); 

    inline size_t calcSize(const std::string& head) { 
     return head.size(); 
    } 

    template<size_t N> 
    size_t calcSize(char const(&) [N]) { 
     return N - 1; 
    } 

    template<typename... Tail> 
    size_t calcSize(const std::string& head, const Tail&... tail) { 
     return head.size() + calcSize(tail...); 
    } 

    template<size_t N, typename... Tail> 
    size_t calcSize(char const(&) [N], const Tail&... tail) { 
     return N - 1 + calcSize(tail...); 
    } 

    template<typename... Args> 
    void fillResult(std::string& result, 
        size_t startIndex, 
        const Args&... args); 

    inline void fillResult(std::string& result, 
          size_t startIndex, 
          const std::string& head) { 
     for (size_t i = 0; i < head.size(); ++i) { 
      if (head[i] == '\0') { 
       break; 
      } 
      result[startIndex++] = head[i]; 
     } 
    } 

    template<size_t N> 
    void fillResult(std::string& result, 
        size_t startIndex, 
        char const(&head) [N]) { 
     for (size_t i = 0; i < N; ++i) { 
      if (head[i] == '\0') { 
       break; 
      } 
      result[startIndex++] = head[i]; 
     } 
    } 

    template<typename... Tail> 
    void fillResult(std::string& result, 
        size_t startIndex, 
        const std::string& head, 
        const Tail&... tail) { 
     for (size_t i = 0; i < head.size(); ++i) { 
      if (head[i] == '\0') { 
       break; 
      } 
      result[startIndex++] = head[i]; 
     } 
     fillResult(result, startIndex, tail...); 
    } 

    template<size_t N, typename... Tail> 
    void fillResult(std::string& result, 
        size_t startIndex, 
        char const(&head) [N], 
        const Tail&... tail) { 
     for (size_t i = 0; i < N; ++i) { 
      if (head[i] == '\0') { 
       break; 
      } 
      result[startIndex++] = head[i]; 
     } 
     fillResult(result, startIndex, tail...); 
    } 
} 

template<typename... Args> 
std::string join(const Args&... args) { 
    std::string result; 
    result.resize(detail::calcSize(args...)); 
    detail::fillResult(result, 0, args...); 
    return result; 
} 


int main() { 
    std::cout << join("ab", "cd", "ef", "gh") << std::endl; 
    std::cout << join(std::string("ab"), std::string("cd"), std::string("ef")) << std::endl; 
    std::cout << join(std::string("ab"), "cd") << std::endl; 
    //std::cout << join(std::string("ab"), "cd", "ef") << std::endl; 
    //std::cout << join(std::string("ab"), "cd", std::string("ef")) << std::endl; 
    return 0; 
} 

它適用於main中的三個第一行,如果您取消註釋兩個最後一個調用中的任何一個,則鏈接器錯誤失敗。用gcc 4.9和clang嘗試了相同的結果。任何人都可以指出什麼是錯的? 這裏是coliru http://coliru.stacked-crooked.com/a/f55aa64fb4861e43

回答

1

我認爲這基本上是一個病態的程序,因爲你最終使用,如果所有重載已在使用的時候已經可見,不會被使用過載的鏈接。這是因爲你想要的超載在實際需要的時候還沒有被實際聲明。爲了解決這個問題,只需添加所有聲明前面:

namespace detail { 

template<typename... Args> 
size_t calcSize(const Args&... args); 

template<size_t N, typename... Tail> 
size_t calcSize(char const(&) [N], const Tail&... tail); 

template<typename... Tail> 
size_t calcSize(const std::string& head, const Tail&... tail); 

template<typename... Tail> 
void fillResult(std::string& result, 
       size_t startIndex, 
       const std::string& head, 
       const Tail&... tail); 

template<size_t N, typename... Tail> 
void fillResult(std::string& result, 
       size_t startIndex, 
       char const(&head) [N], 
       const Tail&... tail); 
+0

[演示](http://coliru.stacked-crooked.com/a/db7d6473fedd0cb5) –

+0

感謝,現在的工作。但我仍然不明白爲什麼它是一個**鏈接器**錯誤,而不是編譯錯誤。 – user6256186

+0

@ user6256186:它不合格,不需要診斷。您的程序在遞歸調用中選擇第一個函數模板'template size_t calcSize(const Args&... args)',而不是定義的過載,並且(選定的專用)未定義該模板。 –