2017-02-02 5 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; 
} 

Он отлично подходит для трех первых линий в главном и не работает с ошибкой компоновщика, если вы раскомментируете любое из двух последних вызовов. Пробовал gcc 4.9 и стучал с тем же результатом. Может ли кто-нибудь указать, что случилось? Вот ссылка на 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

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

+0

Спасибо, он работает сейчас. Но я до сих пор не понимаю, почему это ошибка ** линкер ** вместо ошибки компиляции. – user6256186

+0

@ user6256186: Он плохо сформирован, никакой диагностики не требуется. Ваша программа выбирает первый шаблон функции 'template size_t calcSize (const Args & ... args)' в рекурсивном вызове, а не предполагаемая перегрузка, и (выбранная специализация) этот шаблон не определен. –