2014-04-27 21 views
5

How can I detect if a type can be streamed to an std::ostream? sorusunu aktararak, bazı türlerin bir IO akışına aktarılabileceğini söyleyen bir özellik sınıfı yazdım. Özellik, şu ana kadar bir sorun keşfettiğim gibi işe yaramış görünüyor.Neden nvp :: StringRef için özellik şablon sınıf arama operatörüm << yapamıyorum?

LLVM kullanan bir projenin içindeki kodu kullanıyorum ve StringRef sınıfını kullanıyorum (önerilen std :: string_view öğesine benzer). Here, gerekiyorsa bildirim başlığı dosyasını bulabileceğiniz sınıf için Doxygen dokümanı bağlantısıdır. LLVM, StringRef nesnelerini std akışlarına akış yapmak için bir operatör < < sağladığından (özel bir hafif akış sınıfı kullanıyorlar), bir tane yazdım. Ben özelliği kullandığınızda

Ancak sonra benim özel operatör < < ilan edilirse özelliği (Bir başlığındaki özellik ve operatörü diğerinde < < işlevi var çünkü bu olur) çalışmıyor . Şablon örneklemlerindeki arayışın, örnekleme noktasının bakış açısından çalıştığını düşünürdüm, bu yüzden işe yarayacağını düşündüm. Aslında, aşağıda gördüğünüz gibi, başka bir sınıf ve özel operatörün özelliğinden sonra bildirilen özel bir işleçle < <, her şey beklendiği gibi çalışır (bu yüzden şimdi bu sorunu keşfettim), bu yüzden StringRef'i neyin kastettiğini anlayamıyorum özel. Ben özellik bildirimi önce StringRef için operatör < < ilanını taşırsanız

false 
true 

:

beklentilerimin aksine
#include <iostream> 

#include "llvm/ADT/StringRef.h" 

// Trait class exactly from the cited question's accepted answer 
template<typename T> 
class is_streamable 
{ 
    template<typename SS, typename TT> 
    static auto test(int) 
     -> decltype(std::declval<SS&>() << std::declval<TT>(), 
        std::true_type()); 

    template<typename, typename> 
    static auto test(...) -> std::false_type; 

public: 
    static const bool value = decltype(test<std::ostream,T>(0))::value; 
}; 

// Custom stream operator for StringRef, declared after the trait 
inline std::ostream &operator<<(std::ostream &s, llvm::StringRef const&str) { 
    return s << str.str(); 
} 

// Another example class 
class Foo { }; 
// Same stream operator declared after the trait 
inline std::ostream &operator<<(std::ostream &s, Foo const&) { 
    return s << "LoL\n"; 
} 

int main() 
{ 
    std::cout << std::boolalpha << is_streamable<llvm::StringRef>::value << "\n"; 
    std::cout << std::boolalpha << is_streamable<Foo>::value << "\n"; 

    return 0; 
} 

, bu baskılar:

Bu tam bir örnektir , doğru yazdırır. Peki bu garip şey neden oluyor ve bu sorunu nasıl düzeltebilirim?

+0

Operatörünüzü, ADL'yi etkinleştirmek için türüyle aynı ad alanına koyun. – Yakk

+0

@Yakk Cevap bu, neden bir tane yazmıyorsunuz? – jrok

+1

@jrok bebeğim, kestirmek için uyumak için bebeğim koydum, ve bunu kontrol etmek zor değil, gerçek problem ve detaylandırma vb. :) – Yakk

cevap

1

Yakk tarafından belirtildiği gibi, bu basitçe ADL: Argüman Bağımlı Aramadır.

Rahatsız etmek istemiyorsanız, her zaman argümanlarından en az biri ile aynı ad alanında ücretsiz bir işlev yazmanız gerektiğini unutmayın. Durumunuzda, std işlevlerini eklemek yasaklandığından, işlevinizi llvm ad alanına eklemeniz gerekir. llvm:: ile StringRef argümanına hak kazanmanız gerektiği gerçeğiydi. fonksiyon çözünürlüğü

kurallar oldukça karmaşıktır, ama hızlı kroki olarak:

  • ad arama: potansiyel adaylar
  • aşırı yük çözünürlüğü kümesi toplar: potansiyelleri arasında en iyi aday seçer
  • uzmanlık çözümü: Aday bir işlev şablonuysa,
  • Uygulayabileceğimiz bir uzmanlık olup olmadığını kontrol edin. Burada rned oldukça basittir. Kısaca:
  • argümanın ad alanlarını, ardından ebeveynlerini, ...o zaman global kapsamını
  • ulaşır geçerli kapsamını tarayarak ilerler kadar, sonra üst kapsamı, ... o global alan

Muhtemelen to allow shadowing (gibi başka isim arama için), arama ulaşıncaya kadar Bir karşılaşma ile karşılaştığı ilk kapsamda durur ve çevreleyen herhangi bir alanı yok sayılır.

using yönergelerinin (örneğin using ::operator<<;) başka bir kapsamdaki bir adı tanıtmak için kullanılabileceğini unutmayın. Bu, külfetini müşteriye koyarken külfetlidir, bu yüzden lütfen uygunluğa bir mazeret olarak bakmayın (yaptığımı gördüm: x). shadowing arasında


Örnek: Bu bir belirsizlik hata yükseltmeden "Hello, World" basar. interrupted search ait

#include <iostream> 

namespace hello { namespace world { struct A{}; } } 

namespace hello { void print(world::A) { std::cout << "Hello\n"; } } 

namespace hello { namespace world { void print(A) { std::cout << "Hello, World\n"; } } } 

int main() { 
    hello::world::A a; 
    print(a); 
    return 0; 
} 

Örnek: ::hello::world bir işlev print adlı vermiştir yüzden bunu hiç uymuyor ve ::hello::print kesinlikle iyi bir eşleşme olurdu rağmen dışarı yakalandı.

#include <iostream> 

namespace hello { namespace world { struct A {}; } } 

namespace hello { void print(world::A) { } } 

namespace hello { namespace world { void print() {} } }; 

int main() { 
    hello::world::A a; 
    print(a); // error: too many arguments to function ‘void hello::world::print()’ 
    return 0; 
} 
İlgili konular