farklı nesneler kullanılırken şablon uzmanlığının çoklu tanımı


98

Farklı nesne dosyalarında özel bir şablon kullandığımda, bağlantı kurarken "çoklu tanımlama" hatası alıyorum. Bulduğum tek çözüm "satır içi" işlevini kullanmakla ilgili, ancak bu geçici bir çözüm gibi görünüyor. Bunu "satır içi" anahtar kelimesini kullanmadan nasıl çözebilirim? Bu mümkün değilse neden?

İşte örnek kod:

paulo@aeris:~/teste/cpp/redef$ cat hello.h 
#ifndef TEMPLATE_H
#define TEMPLATE_H

#include <iostream>

template <class T>
class Hello
{
public:
    void print_hello(T var);
};

template <class T>
void Hello<T>::print_hello(T var)
{
    std::cout << "Hello generic function " << var << "\n";
}

template <> //inline
void Hello<int>::print_hello(int var)
{
    std::cout << "Hello specialized function " << var << "\n";
}

#endif

paulo@aeris:~/teste/cpp/redef$ cat other.h 
#include <iostream>

void other_func();

paulo@aeris:~/teste/cpp/redef$ cat other.c 
#include "other.h"

#include "hello.h"

void other_func()
{
    Hello<char> hc;
    Hello<int> hi;

    hc.print_hello('a');
    hi.print_hello(1);
}

paulo@aeris:~/teste/cpp/redef$ cat main.c 
#include "hello.h"

#include "other.h"

int main()
{
    Hello<char> hc;
    Hello<int> hi;

    hc.print_hello('a');
    hi.print_hello(1);

    other_func();

    return 0;
}

paulo@aeris:~/teste/cpp/redef$ cat Makefile
all:
    g++ -c other.c -o other.o -Wall -Wextra
    g++ main.c other.o -o main -Wall -Wextra

En sonunda:

paulo@aeris:~/teste/cpp/redef$ make
g++ -c other.c -o other.o -Wall -Wextra
g++ main.c other.o -o main -Wall -Wextra
other.o: In function `Hello<int>::print_hello(int)':
other.c:(.text+0x0): multiple definition of `Hello<int>::print_hello(int)'
/tmp/cc0dZS9l.o:main.c:(.text+0x0): first defined here
collect2: ld returned 1 exit status
make: ** [all] Erro 1

Hello.h içindeki "satır içi" açıklamasını kaldırırsam, kod derlenecek ve çalışacak, ancak bu bana bir tür "geçici çözüm" gibi görünüyor: ya özel işlev büyükse ve birçok kez kullanılıyorsa? Büyük bir ikili alacak mıyım? Bunu yapmanın başka bir yolu var mı? Varsa nasıl? Değilse neden?

Cevapları aramaya çalıştım, ancak sahip olduğum tek şey daha fazla açıklama yapmadan "satır içi kullanım" oldu.

Teşekkürler


7
gerçek özel uygulamayı başlık dosyası yerine
.cpp'ye koyun

Yanıtlar:


133

Sezgisel olarak, bir şeyi tamamen uzmanlaştırdığınızda, bu artık bir şablon parametresine bağlı değildir - bu nedenle uzmanlığı satır içi yapmazsanız, onu .h yerine bir .cpp dosyasına koymanız gerekir veya sonunda David'in söylediği gibi bir tanım kuralı. Şablonları kısmen uzmanlaştırdığınızda, kısmi uzmanlaşmaların bir veya daha fazla şablon parametresine bağlı olduğunu ve bu nedenle yine de bir .h dosyasında yer aldığını unutmayın.


Hmmm ODR'yi nasıl bozduğu konusunda hala biraz kafam karıştı. Çünkü tamamen özelleştirilmiş şablonu yalnızca bir kez tanımlarsınız. Nesneyi farklı nesne dosyalarında birden çok kez oluşturuyor olabilirsiniz (yani bu durumda diğer.c ve main.c'de somutlaştırılmıştır) ancak orijinal nesnenin kendisi yalnızca bir dosyada tanımlanır - bu durumda hello.h.
Justin Liang

3
@JustinLiang: Başlık iki ayrı .c dosyasında yer alır - bu, içeriğini (tüm uzmanlık dahil) doğrudan ilgili yerlerde bulunduğu dosyalara yazmışsınız gibi aynı etkiye sahiptir. Tek Tanım Kuralı (bkz. En.wikipedia.org/wiki/One_Definition_Rule ) (diğer şeylerin yanı sıra) şöyle der: "Programın tamamında, bir nesne veya satır içi olmayan işlevin birden fazla tanımı olamaz". Bu durumda, işlev şablonunun tam uzmanlığı özünde normal bir işlev gibidir, bu nedenle satır içi olmadığı sürece birden fazla tanımı olamaz.
Stuart Golodetz

Hmmm, şablonlu bir uzmanlığımız olmadığında bu hatanın görünmeyeceğini fark ettim. Diyelim ki başlık dosyasında tanımlanmış iki farklı fonksiyonumuz vardı, sınıfın dışında, hala satır içi olmadan çalışacaklar mı? Örneğin: pastebin.com/raw.php?i=bRaiNC7M . O dersi aldım ve iki dosyaya ekledim. Bu, "içeriği doğrudan iki dosyaya yazmışsınız gibi aynı etkiye" sahip olmaz mıydı ve bu nedenle çoklu tanımlama hatası olur mu?
Justin Liang

@ Justin Liang, sınıf tabanlı başlık kodunuz, işlev tanımları sınıfın gövdesinin içinde olmadığı sürece, birden çok dosyaya eklenmişse ODR'yi ihlal etmeye devam edecektir.
haripkannan

Öyleyse, statik üye tanımımdan önce gelirse, template <typename T>o zaman bir başlığa gidebilir ve eğer template<>öyleyse olmayabilir?
Violet Giraffe

50

Anahtar sözcük inline, derleyiciye, derleyicinin yapmaya ya da yapmamaya karar verebileceği gerçek satır içi işleminden çok, Bir Tanım Kuralı'nı ihlal etmeden sembolün birden fazla nesne dosyasında mevcut olacağını söylemekle ilgilidir.

Gördüğünüz sorun, satır içi olmadan işlevin, ODR'yi ihlal edecek şekilde başlığı içeren tüm çeviri birimlerinde derleneceğidir. inlineOraya eklemek , gitmenin doğru yoludur. Aksi takdirde, başka herhangi bir işlevde yaptığınız gibi uzmanlığı iletebilir ve tek bir çeviri biriminde sağlayabilirsiniz.


22

Başlığınızda ( void Hello<T>::print_hello(T var)) açıkça bir şablon oluşturdunuz . Bu, birden çok tanım oluşturacaktır. Bunu iki şekilde çözebilirsiniz:

1) Örneklemenizi satır içi yapın.

2) Örneklemeyi bir başlıkta bildirin ve sonra bunu bir cpp'de uygulayın.


Aslında bunları isimsiz bir ad alanına koymanın üçüncü bir yolu var ... bu da C'de statik olmasına benzer
Alexis Wilke

4
Bu burada geçerli değil. Bir şablon uzmanlığının, orijinal şablonla aynı ad alanında olması gerekir.
Edward Strange

0

İşte bazı olduğu parça bu sorunla ilgili 11 standardına ++ C:

Bir işlev şablonunun açık bir uzmanlığı, yalnızca satır içi belirtici ile bildirilirse veya silinmiş olarak tanımlanırsa ve işlev şablonunun satır içi olup olmadığından bağımsız olarak satır içi olur. [ Misal:

şablon void f (T) {/ * ... /} şablon satır içi T g (T) {/ ... * /}

şablon <> satır içi void f <> (int) {/ * ... /} // Tamam: satır içi şablon <> int g <> (int) {/ ... * /} // Tamam: satır içi değil - son misal ]

Dolayısıyla, *.hdosyada şablonlarla ilgili bazı açık (aka tam) uzmanlıklar inlineyaparsanız, ODR ihlalinden kurtulmanıza yine de yardım etmeniz gerekecektir .

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.